Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions apps/gpui/test/gpui/template/components_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,41 @@ defmodule GPUI.Template.ComponentsTest do
end
end

test "search-first dialogs hide their heading and retain a controlled top offset" do
dialog =
~GPUI"""
<Overlay.dialog id="palette" title="Search" show_title={false} close_button={false} margin_top={112}>
<:content><text>Search results</text></:content>
</Overlay.dialog>
"""

refute dialog.attrs[:show_title]
assert dialog.attrs[:title] == "Search"
assert dialog.attrs[:margin_top] == 112

default_dialog =
~GPUI"""
<Overlay.dialog id="default-dialog" title="Settings">
<:content><text>Settings content</text></:content>
</Overlay.dialog>
"""

assert default_dialog.attrs[:show_title]
assert default_dialog.attrs[:margin_top] == nil

for margin <- [-1, 4097, "80", false] do
assigns = %{margin: margin}

assert_raise ArgumentError, ~r/margin_top/, fn ->
~GPUI"""
<Overlay.dialog id="palette" title="Search" margin_top={assigns.margin}>
<:content><text>Search results</text></:content>
</Overlay.dialog>
"""
end
end
end

test "dropdown menus compile repeated textual item slots" do
assert %GPUI.Element{
type: :ui_dropdown_menu,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,9 @@ defmodule GPUI.Components.Schema.Declarations do
id: :string,
open: :boolean,
title: :required_string,
show_title: {:default, :boolean, true},
width: {:default, :number, 448.0},
margin_top: :number,
overlay: {:default, :boolean, true},
closable: {:default, :boolean, true},
keyboard: {:default, :boolean, true},
Expand Down
17 changes: 15 additions & 2 deletions apps/gpui_components/lib/gpui/ui/overlay.ex
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ defmodule GPUI.UI.Overlay do
focus while open and restores the previous focus when it closes. Escape and
overlay clicks request closure when enabled.

Set `show_title: false` for search-first dialogs without a visible heading.
`title` still names the dialog contract and optional trigger. `margin_top`
controls the dialog's distance from the top of the window; omitting it retains
the native viewport-relative placement.

#{ComponentsSchema.component_options_doc(:ui_dialog)}
"""
@spec dialog(dialog_options()) :: Element.t()
Expand Down Expand Up @@ -266,16 +271,24 @@ defmodule GPUI.UI.Overlay do
raise ArgumentError, "dialog content must use :trigger and :content named slots"
end

boolean_attrs = Enum.map([:open, :overlay, :closable, :keyboard, :close_button], &assigns[&1])
boolean_attrs =
Enum.map([:open, :show_title, :overlay, :closable, :keyboard, :close_button], &assigns[&1])

unless Enum.all?(boolean_attrs, &is_boolean/1) do
raise ArgumentError,
"dialog open, overlay, closable, keyboard, and close_button must be booleans"
"dialog open, show_title, overlay, closable, keyboard, and close_button must be booleans"
end

unless is_number(assigns.width) and assigns.width > 0 and assigns.width <= 4096 do
raise ArgumentError, "dialog width must be greater than 0 and at most 4096"
end

margin_top = assigns[:margin_top]

unless is_nil(margin_top) or
(is_number(margin_top) and margin_top >= 0 and margin_top <= 4096) do
raise ArgumentError, "dialog margin_top must be between 0 and 4096"
end
end

defp tooltip_text!(children), do: textual_slot!(children, "tooltip :content")
Expand Down
4 changes: 4 additions & 0 deletions apps/gpui_components/native/src/generated/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2576,7 +2576,9 @@ pub(crate) struct DialogComponentNode {
pub(crate) id: String,
pub(crate) open: bool,
pub(crate) title: String,
pub(crate) show_title: bool,
pub(crate) width: f64,
pub(crate) margin_top: Option<f64>,
pub(crate) overlay: bool,
pub(crate) closable: bool,
pub(crate) keyboard: bool,
Expand All @@ -2595,7 +2597,9 @@ pub(crate) fn decode_generated_dialog_component<'a>(
id: component_id(term)?,
open: component_bool_attr(term, atoms::open())?.unwrap_or(false),
title: component_required_string_attr(term, atoms::title())?,
show_title: component_bool_attr(term, atoms::show_title())?.unwrap_or(true),
width: component_number_attr(term, atoms::width())?.unwrap_or(448.0),
margin_top: component_number_attr(term, atoms::margin_top())?,
overlay: component_bool_attr(term, atoms::overlay())?.unwrap_or(true),
closable: component_bool_attr(term, atoms::closable())?.unwrap_or(true),
keyboard: component_bool_attr(term, atoms::keyboard())?.unwrap_or(true),
Expand Down
20 changes: 19 additions & 1 deletion apps/gpui_native/native/src/element/component/overlay/dialog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub(crate) fn render_dialog(
ComponentDialog, ComponentOverlayState, DialogConfig,
};
use crate::element::controlled::ControlledBinding;
use gpui::prelude::FluentBuilder as _;
use gpui::{
AppContext, InteractiveElement, IntoElement, MouseButton, ParentElement, Role,
StatefulInteractiveElement,
Expand All @@ -44,7 +45,11 @@ pub(crate) fn render_dialog(
let content_focus = context.cx.focus_handle();
let config = Arc::new(Mutex::new(DialogConfig {
title: node.title.clone(),
show_title: node.show_title,
width: node.width.clamp(1.0, 4096.0) as f32,
margin_top: node
.margin_top
.map(|margin| margin.clamp(0.0, 4096.0) as f32),
overlay: node.overlay,
closable: node.closable,
keyboard: node.keyboard,
Expand Down Expand Up @@ -137,7 +142,11 @@ pub(crate) fn render_dialog(
if let Ok(mut config) = dialog.config.lock() {
*config = DialogConfig {
title: node.title.clone(),
show_title: node.show_title,
width: node.width.clamp(1.0, 4096.0) as f32,
margin_top: node
.margin_top
.map(|margin| margin.clamp(0.0, 4096.0) as f32),
overlay: node.overlay,
closable: node.closable,
keyboard: node.keyboard,
Expand Down Expand Up @@ -201,7 +210,9 @@ pub(crate) fn render_dialog(
.map(|config| config.clone())
.unwrap_or(DialogConfig {
title: "Dialog".to_string(),
show_title: true,
width: 448.0,
margin_top: None,
overlay: true,
closable: true,
keyboard: true,
Expand All @@ -210,6 +221,9 @@ pub(crate) fn render_dialog(
});
let dialog = super::super::apply_component_styles(dialog, config.style)
.width(gpui::px(config.width))
.when_some(config.margin_top, |dialog, margin| {
dialog.margin_top(gpui::px(margin))
})
.overlay(config.overlay)
.overlay_closable(config.closable)
.keyboard(config.keyboard && config.closable)
Expand Down Expand Up @@ -242,7 +256,11 @@ pub(crate) fn render_dialog(
}
})
.child(content_view.clone());
dialog.title(config.title)
if config.show_title {
dialog.title(config.title)
} else {
dialog
}
});
window.defer(cx, move |window, cx| content_focus.focus(window, cx));
});
Expand Down
2 changes: 2 additions & 0 deletions apps/gpui_native/native/src/element/component_registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,9 @@ pub(crate) struct ComponentCombobox {
#[derive(Clone)]
pub(crate) struct DialogConfig {
pub(crate) title: String,
pub(crate) show_title: bool,
pub(crate) width: f32,
pub(crate) margin_top: Option<f32>,
pub(crate) overlay: bool,
pub(crate) closable: bool,
pub(crate) keyboard: bool,
Expand Down
20 changes: 10 additions & 10 deletions apps/gpui_native/native/src/generated/atoms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,16 +87,16 @@ mod atoms {
search_placeholder = "search_placeholder", select = "select", selectable =
"selectable", selected = "selected", selected_column = "selected_column",
selected_index = "selected_index", selection = "selection", set_size =
"set_size", show_line_numbers = "show_line_numbers", show_whitespaces =
"show_whitespaces", side = "side", size = "size", sizes = "sizes", soft_wrap =
"soft_wrap", sort = "sort", sort_column = "sort_column", sort_direction =
"sort_direction", sortable = "sortable", stale_revision = "stale_revision",
status = "status", step = "step", stride = "stride", style = "style", style_runs
= "style_runs", submit = "submit", submit_policy = "submit_policy", tab_size =
"tab_size", tab_width = "tab_width", text = "text", text_align = "text_align",
text_buffer_lock_failed = "text_buffer_lock_failed", text_overflow =
"text_overflow", title = "title", toggle = "toggle", top = "top", total_count =
"total_count", transaction = "transaction", transaction_conflict =
"set_size", show_line_numbers = "show_line_numbers", show_title = "show_title",
show_whitespaces = "show_whitespaces", side = "side", size = "size", sizes =
"sizes", soft_wrap = "soft_wrap", sort = "sort", sort_column = "sort_column",
sort_direction = "sort_direction", sortable = "sortable", stale_revision =
"stale_revision", status = "status", step = "step", stride = "stride", style =
"style", style_runs = "style_runs", submit = "submit", submit_policy =
"submit_policy", tab_size = "tab_size", tab_width = "tab_width", text = "text",
text_align = "text_align", text_buffer_lock_failed = "text_buffer_lock_failed",
text_overflow = "text_overflow", title = "title", toggle = "toggle", top = "top",
total_count = "total_count", transaction = "transaction", transaction_conflict =
"transaction_conflict", truncate = "truncate", type_atom = "type", value =
"value", vanilla = "vanilla", variant = "variant", viewport = "viewport",
white_space = "white_space", width = "width", window_blur = "window_blur",
Expand Down
4 changes: 4 additions & 0 deletions apps/gpui_native/native/src/generated/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2576,7 +2576,9 @@ pub(crate) struct DialogComponentNode {
pub(crate) id: String,
pub(crate) open: bool,
pub(crate) title: String,
pub(crate) show_title: bool,
pub(crate) width: f64,
pub(crate) margin_top: Option<f64>,
pub(crate) overlay: bool,
pub(crate) closable: bool,
pub(crate) keyboard: bool,
Expand All @@ -2595,7 +2597,9 @@ pub(crate) fn decode_generated_dialog_component<'a>(
id: component_id(term)?,
open: component_bool_attr(term, atoms::open())?.unwrap_or(false),
title: component_required_string_attr(term, atoms::title())?,
show_title: component_bool_attr(term, atoms::show_title())?.unwrap_or(true),
width: component_number_attr(term, atoms::width())?.unwrap_or(448.0),
margin_top: component_number_attr(term, atoms::margin_top())?,
overlay: component_bool_attr(term, atoms::overlay())?.unwrap_or(true),
closable: component_bool_attr(term, atoms::closable())?.unwrap_or(true),
keyboard: component_bool_attr(term, atoms::keyboard())?.unwrap_or(true),
Expand Down
19 changes: 19 additions & 0 deletions apps/gpui_native/test/test/gpui/test/native/navigation_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ defmodule GPUI.Test.Native.NavigationTest do
id="settings-dialog"
open={assigns.open}
title="Settings"
show_title={Map.get(assigns, :show_title, true)}
width={280}
margin_top={Map.get(assigns, :margin_top)}
phx-change="dialog_changed"
>
<:trigger>
Expand Down Expand Up @@ -115,6 +117,23 @@ defmodule GPUI.Test.Native.NavigationTest do
assert_receive {:gpui, ^ui, {:event, %{type: :click, event: "dialog_action"}}}
end

test "search-first dialogs omit the heading and retain content activation and Escape", %{ui: ui} do
render(ui, DialogView, open: true, margin_top: 24)
settle(ui)
titled_y = bounds(ui, "dialog-action").y

render(ui, DialogView, open: false, margin_top: 24)
settle(ui)
render(ui, DialogView, open: true, show_title: false, margin_top: 24)
settle(ui)
assert bounds(ui, "dialog-action").y < titled_y

click(ui, "dialog-action")
assert_receive {:gpui, ^ui, {:event, %{type: :click, event: "dialog_action"}}}
press(ui, :escape)
assert_receive {:gpui, ^ui, {:event, %{type: :change, event: "dialog_changed", value: false}}}
end

test "dialog routes Escape closure and restores its trigger focus", %{ui: ui} do
render(ui, DialogView, open: false)
focus(ui, "settings-dialog")
Expand Down
4 changes: 4 additions & 0 deletions codegen/gpui/codegen/native/component/macros.ex
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ defmodule GPUI.Codegen.Native.Component.Macros do
when type in [:number, :positive_number, :unit_number, :edge_fade_size],
do: quote(do: R.f64())

defp component_field_type(_name, :number), do: quote(do: R.option(R.f64()))
defp component_field_type(_name, :non_negative_integer), do: quote(do: R.option(R.u64()))

defp component_field_type(_name, {:default, type, _value})
Expand Down Expand Up @@ -160,6 +161,9 @@ defmodule GPUI.Codegen.Native.Component.Macros do
defp component_decoder_expr(name, :non_negative_integer),
do: quote(do: unwrap!(component_non_negative_integer_attr(term, unquote(atom_call(name)))))

defp component_decoder_expr(name, :number),
do: quote(do: unwrap!(component_number_attr(term, unquote(atom_call(name)))))

defp component_decoder_expr(name, {:default, type, default})
when type in [:non_negative_integer, :positive_integer] do
helper =
Expand Down
Loading