mirror of
https://github.com/zed-industries/zed.git
synced 2026-07-09 16:00:35 +00:00
picker: Simplify presentation and sizing APIs, add preview-aware defaults (#59719)
Some checks are pending
Congratsbot / check-author (push) Waiting to run
Congratsbot / congrats (push) Blocked by required conditions
deploy_nightly_docs / deploy_docs (push) Waiting to run
run_tests / orchestrate (push) Waiting to run
run_tests / check_style (push) Waiting to run
run_tests / clippy_windows (push) Blocked by required conditions
run_tests / clippy_linux (push) Blocked by required conditions
run_tests / clippy_mac (push) Blocked by required conditions
run_tests / clippy_mac_x86_64 (push) Blocked by required conditions
run_tests / run_tests_windows (push) Blocked by required conditions
run_tests / run_tests_linux (push) Blocked by required conditions
run_tests / run_tests_mac (push) Blocked by required conditions
run_tests / miri_scheduler (push) Blocked by required conditions
run_tests / doctests (push) Blocked by required conditions
run_tests / check_workspace_binaries (push) Blocked by required conditions
run_tests / build_visual_tests_binary (push) Blocked by required conditions
run_tests / check_wasm (push) Blocked by required conditions
run_tests / check_dependencies (push) Blocked by required conditions
run_tests / check_docs (push) Blocked by required conditions
run_tests / check_licenses (push) Blocked by required conditions
run_tests / check_scripts (push) Blocked by required conditions
run_tests / check_postgres_and_protobuf_migrations (push) Blocked by required conditions
run_tests / extension_tests (push) Blocked by required conditions
run_tests / tests_pass (push) Blocked by required conditions
Some checks are pending
Congratsbot / check-author (push) Waiting to run
Congratsbot / congrats (push) Blocked by required conditions
deploy_nightly_docs / deploy_docs (push) Waiting to run
run_tests / orchestrate (push) Waiting to run
run_tests / check_style (push) Waiting to run
run_tests / clippy_windows (push) Blocked by required conditions
run_tests / clippy_linux (push) Blocked by required conditions
run_tests / clippy_mac (push) Blocked by required conditions
run_tests / clippy_mac_x86_64 (push) Blocked by required conditions
run_tests / run_tests_windows (push) Blocked by required conditions
run_tests / run_tests_linux (push) Blocked by required conditions
run_tests / run_tests_mac (push) Blocked by required conditions
run_tests / miri_scheduler (push) Blocked by required conditions
run_tests / doctests (push) Blocked by required conditions
run_tests / check_workspace_binaries (push) Blocked by required conditions
run_tests / build_visual_tests_binary (push) Blocked by required conditions
run_tests / check_wasm (push) Blocked by required conditions
run_tests / check_dependencies (push) Blocked by required conditions
run_tests / check_docs (push) Blocked by required conditions
run_tests / check_licenses (push) Blocked by required conditions
run_tests / check_scripts (push) Blocked by required conditions
run_tests / check_postgres_and_protobuf_migrations (push) Blocked by required conditions
run_tests / extension_tests (push) Blocked by required conditions
run_tests / tests_pass (push) Blocked by required conditions
The recent "pickers with previews" overhaul left the picker
sizing/presentation API spread across several overlapping knobs that
every call site had to set correctly — and many didn't, causing pickers
across the app to render at the wrong width, lose their container, or
stop dismissing. This PR consolidates that surface into a small,
hard-to-misuse API and makes correct sizing the default.
Net effect: a plain `Picker::uniform_list(delegate, …)` now renders
correctly out of the box (standard width, standard max-height, shrinks
to fit, dismisses properly), and the ~35 call sites only specify what
genuinely differs.
## API changes
**Presentation** — three overlapping booleans (`is_modal`, `is_popover`,
`is_resizable`) collapsed into one enum, with resizability living inside
the only variant where it's meaningful:
```rust
enum Presentation {
Modal { resizable: bool }, // own chrome, dismisses on blur, optionally resizable
Popover, // own chrome, dismisses on blur, never resizable
Embedded, // host container owns chrome + dismissal
}
```
- `modal(bool)` is **removed** in favor of explicit, self-documenting
builders:
- *(default)* → `Modal` (resizable iff it has a preview)
- `.popover()` → `Popover` (menu-attached surfaces)
- `.embedded()` → `Embedded` (pickers nested in a larger modal/view)
- Dynamic callers use `.when(cond, Picker::embedded)` (added `impl
FluentBuilder for Picker`).
**Sizing** — preview-vs-not now drives everything; the manual padding
knob is gone:
| Before | After |
|---|---|
| `vertical_padding` field + `no_vertical_padding()` | removed — derived
from whether a preview is visible |
| `height(...)` (ambiguous: fixed vs max) | `max_height(...)` (plain
pickers shrink-to-fit, capped here) |
| `minimum_results_width(...)` | removed — a plain picker's min width
tracks its opening width; preview pickers use standard internal pane
mins |
| default size = 60% viewport | default = `DEFAULT_MODAL_WIDTH` (34rem)
× `DEFAULT_MODAL_MAX_HEIGHT` (24rem, max) |
| resize handles gated on `is_modal` | gated on `is_resizable` (new
`resizable(bool)` builder; auto-`true` for preview pickers) |
Call sites now only override the exceptions: narrow popover selectors
(`initial_width`), the taller outline view (`max_height`), and preview
pickers (constructed via `*_with_preview`).
## Behavior fixes
- **Wrong widths everywhere**: pickers were falling back to
60%-of-viewport because the original migration set
`minimum_results_width` but never `initial_width`. Fixed at the source
via the new defaults.
- **Popovers had no container and wouldn't dismiss**: `is_modal=false`
was suppressing both the elevated background *and* blur-dismiss. Split
out so popovers keep their chrome and dismiss on click-away/escape. This
fixed the agent-panel model/profile selectors, sidebar recent projects,
and the settings theme/font/icon/ollama pickers (which were incorrectly
using `modal(false)`).
- **Sidebar recent projects stretched to full height**: was missing the
shrink-to-fit behavior; now capped and content-sized like other
popovers.
- **Preview crash**: removed an over-strict `debug_assert!` that
panicked when previewing an empty file (`message == None && editor
empty` is valid).
- **Preview-aware default size**: pickers open at standard width with
the preview hidden, and expand to the larger "telescope" size when a
preview is shown. Fixes the text finder rendering super-wide by default,
and makes the file finder expand (rather than cram its results) when you
toggle the preview.
---
Release Notes:
- N/A
This commit is contained in:
parent
4eb039b451
commit
f88bc7e18a
61 changed files with 286 additions and 374 deletions
|
|
@ -451,7 +451,7 @@
|
|||
"ctrl-shift-h": "search::ToggleReplace",
|
||||
"alt-ctrl-g": "search::ToggleRegex",
|
||||
"alt-ctrl-x": "search::ToggleRegex",
|
||||
"ctrl-alt-p": "project_search::OpenTextFinder",
|
||||
"ctrl-alt-f": "project_search::OpenTextFinder",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -481,7 +481,7 @@
|
|||
"ctrl-shift-h": "search::ToggleReplace",
|
||||
"alt-ctrl-g": "search::ToggleRegex",
|
||||
"alt-ctrl-x": "search::ToggleRegex",
|
||||
"ctrl-alt-p": "project_search::OpenTextFinder",
|
||||
"ctrl-alt-f": "project_search::OpenTextFinder",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -508,7 +508,7 @@
|
|||
"cmd-shift-h": "search::ToggleReplace",
|
||||
"alt-cmd-g": "search::ToggleRegex",
|
||||
"alt-cmd-x": "search::ToggleRegex",
|
||||
"cmd-alt-p": "project_search::OpenTextFinder",
|
||||
"alt-cmd-f": "project_search::OpenTextFinder",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -542,7 +542,7 @@
|
|||
"cmd-shift-h": "search::ToggleReplace",
|
||||
"alt-cmd-g": "search::ToggleRegex",
|
||||
"alt-cmd-x": "search::ToggleRegex",
|
||||
"cmd-alt-p": "project_search::OpenTextFinder",
|
||||
"alt-cmd-f": "project_search::OpenTextFinder",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -452,7 +452,7 @@
|
|||
"ctrl-shift-f": "search::FocusSearch",
|
||||
"ctrl-shift-h": "search::ToggleReplace",
|
||||
"alt-r": "search::ToggleRegex", // vscode
|
||||
"ctrl-alt-p": "project_search::OpenTextFinder",
|
||||
"ctrl-alt-f": "project_search::OpenTextFinder",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -483,7 +483,7 @@
|
|||
"escape": "project_search::ToggleFocus",
|
||||
"ctrl-shift-h": "search::ToggleReplace",
|
||||
"alt-r": "search::ToggleRegex", // vscode
|
||||
"ctrl-alt-p": "project_search::OpenTextFinder",
|
||||
"ctrl-alt-f": "project_search::OpenTextFinder",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@
|
|||
"context": "TextFinder || (TextFinder > Picker > Editor) || (TextFinder > Picker > menu)",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"cmd-alt-p": "text_finder::ToProjectSearch",
|
||||
"alt-cmd-f": "text_finder::ToProjectSearch",
|
||||
"cmd-j": "pane::SplitDown",
|
||||
"cmd-k": "pane::SplitUp",
|
||||
"cmd-h": "pane::SplitLeft",
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
{
|
||||
"context": "TextFinder || (TextFinder > Picker > Editor) || (TextFinder > Picker > menu)",
|
||||
"bindings": {
|
||||
"ctrl-alt-p": "text_finder::ToProjectSearch",
|
||||
"ctrl-alt-f": "text_finder::ToProjectSearch",
|
||||
"ctrl-j": "pane::SplitDown",
|
||||
"ctrl-k": "pane::SplitUp",
|
||||
"ctrl-h": "pane::SplitLeft",
|
||||
|
|
|
|||
|
|
@ -295,7 +295,7 @@ impl ManageProfilesModal {
|
|||
window,
|
||||
cx,
|
||||
)
|
||||
.modal(false)
|
||||
.embedded()
|
||||
});
|
||||
|
||||
let dismiss_subscription = cx.subscribe_in(&model_picker, window, {
|
||||
|
|
|
|||
|
|
@ -25,14 +25,7 @@ impl ToolPicker {
|
|||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Self {
|
||||
let picker = cx.new(|cx| {
|
||||
Picker::uniform_list(delegate, window, cx)
|
||||
.modal(false)
|
||||
.initial_width(rems(34.))
|
||||
.minimum_results_width(rems(34.))
|
||||
.height(rems(24.))
|
||||
.no_vertical_padding()
|
||||
});
|
||||
let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx).embedded());
|
||||
Self { picker }
|
||||
}
|
||||
|
||||
|
|
@ -41,14 +34,7 @@ impl ToolPicker {
|
|||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Self {
|
||||
let picker = cx.new(|cx| {
|
||||
Picker::list(delegate, window, cx)
|
||||
.modal(false)
|
||||
.initial_width(rems(34.))
|
||||
.minimum_results_width(rems(34.))
|
||||
.height(rems(24.))
|
||||
.no_vertical_padding()
|
||||
});
|
||||
let picker = cx.new(|cx| Picker::list(delegate, window, cx).embedded());
|
||||
Self { picker }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -299,9 +299,7 @@ impl ConfigOptionSelector {
|
|||
Picker::nonsearchable_list(delegate, window, picker_cx)
|
||||
}
|
||||
.show_scrollbar(true)
|
||||
.minimum_results_width(rems(20.))
|
||||
.height(rems(20.))
|
||||
.no_vertical_padding()
|
||||
.initial_width(rems(20.))
|
||||
})
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -47,15 +47,12 @@ pub fn language_model_selector(
|
|||
if popover_styles {
|
||||
Picker::list(delegate, window, cx)
|
||||
.show_scrollbar(true)
|
||||
.minimum_results_width(rems(20.))
|
||||
.height(rems(20.))
|
||||
.no_vertical_padding()
|
||||
.modal(false)
|
||||
.initial_width(rems(20.))
|
||||
.popover()
|
||||
} else {
|
||||
Picker::list(delegate, window, cx)
|
||||
.show_scrollbar(true)
|
||||
.minimum_results_width(rems(20.))
|
||||
.initial_width(rems(20.))
|
||||
.embedded()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,9 +35,7 @@ pub fn acp_model_selector(
|
|||
let delegate = ModelPickerDelegate::new(selector, focus_handle, window, cx);
|
||||
Picker::list(delegate, window, cx)
|
||||
.show_scrollbar(true)
|
||||
.minimum_results_width(rems(20.))
|
||||
.height(rems(20.))
|
||||
.no_vertical_padding()
|
||||
.initial_width(rems(20.))
|
||||
}
|
||||
|
||||
enum ModelPickerEntry {
|
||||
|
|
|
|||
|
|
@ -123,9 +123,7 @@ impl ProfileSelector {
|
|||
let picker = cx.new(|cx| {
|
||||
Picker::list(delegate, window, cx)
|
||||
.show_scrollbar(true)
|
||||
.minimum_results_width(rems(18.))
|
||||
.height(rems(20.))
|
||||
.no_vertical_padding()
|
||||
.initial_width(rems(18.))
|
||||
});
|
||||
|
||||
self.picker = Some(picker);
|
||||
|
|
|
|||
|
|
@ -1134,11 +1134,7 @@ impl ProjectPickerModal {
|
|||
let picker = cx.new(|cx| {
|
||||
Picker::list(delegate, window, cx)
|
||||
.list_measure_all()
|
||||
.modal(false)
|
||||
.initial_width(rems(34.))
|
||||
.minimum_results_width(rems(34.))
|
||||
.height(rems(24.))
|
||||
.no_vertical_padding()
|
||||
.embedded()
|
||||
});
|
||||
|
||||
let picker_focus_handle = picker.focus_handle(cx);
|
||||
|
|
|
|||
|
|
@ -65,11 +65,7 @@ impl ChannelModal {
|
|||
window,
|
||||
cx,
|
||||
)
|
||||
.modal(false)
|
||||
.initial_width(rems(34.))
|
||||
.minimum_results_width(rems(34.))
|
||||
.height(rems(24.))
|
||||
.no_vertical_padding()
|
||||
.embedded()
|
||||
});
|
||||
|
||||
Self {
|
||||
|
|
|
|||
|
|
@ -21,14 +21,7 @@ impl ContactFinder {
|
|||
potential_contacts: Arc::from([]),
|
||||
selected_index: 0,
|
||||
};
|
||||
let picker = cx.new(|cx| {
|
||||
Picker::uniform_list(delegate, window, cx)
|
||||
.modal(false)
|
||||
.initial_width(rems(34.))
|
||||
.minimum_results_width(rems(34.))
|
||||
.height(rems(24.))
|
||||
.no_vertical_padding()
|
||||
});
|
||||
let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx).embedded());
|
||||
|
||||
Self { picker }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -125,11 +125,7 @@ impl CommandPalette {
|
|||
);
|
||||
|
||||
let picker = cx.new(|cx| {
|
||||
let picker = Picker::uniform_list(delegate, window, cx)
|
||||
.initial_width(rems(34.))
|
||||
.minimum_results_width(rems(34.))
|
||||
.height(rems(24.))
|
||||
.no_vertical_padding();
|
||||
let picker = Picker::uniform_list(delegate, window, cx);
|
||||
picker.set_query(query, window, cx);
|
||||
picker
|
||||
});
|
||||
|
|
|
|||
|
|
@ -103,11 +103,7 @@ impl AttachModal {
|
|||
window,
|
||||
cx,
|
||||
)
|
||||
.modal(modal)
|
||||
.initial_width(rems(34.))
|
||||
.minimum_results_width(rems(34.))
|
||||
.height(rems(24.))
|
||||
.no_vertical_padding()
|
||||
.when(!modal, |picker| picker.embedded())
|
||||
});
|
||||
Self {
|
||||
_subscription: cx.subscribe(&picker, |_, _, _, cx| {
|
||||
|
|
|
|||
|
|
@ -106,9 +106,7 @@ impl NewProcessModal {
|
|||
let delegate =
|
||||
DebugDelegate::new(debug_panel.downgrade(), task_store.clone());
|
||||
Picker::list(delegate, window, cx)
|
||||
.modal(false)
|
||||
.height(rems(24.))
|
||||
.no_vertical_padding()
|
||||
.embedded()
|
||||
.list_measure_all()
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1184,8 +1184,7 @@ impl StatefulModal for DevContainerModal {
|
|||
}),
|
||||
);
|
||||
|
||||
let picker =
|
||||
cx.new(|cx| Picker::uniform_list(delegate, window, cx).modal(false));
|
||||
let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx).embedded());
|
||||
self.picker = Some(picker);
|
||||
Some(DevContainerState::TemplateQueryReturned(Ok(items)))
|
||||
} else {
|
||||
|
|
@ -1316,8 +1315,7 @@ impl StatefulModal for DevContainerModal {
|
|||
}),
|
||||
);
|
||||
|
||||
let picker =
|
||||
cx.new(|cx| Picker::uniform_list(delegate, window, cx).modal(false));
|
||||
let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx).embedded());
|
||||
self.features_picker = Some(picker);
|
||||
Some(DevContainerState::FeaturesQueryReturned(template_entry))
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -95,13 +95,7 @@ impl EncodingSelector {
|
|||
|
||||
fn new(buffer: Entity<Buffer>, window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||
let delegate = EncodingSelectorDelegate::new(cx.entity().downgrade(), buffer);
|
||||
let picker = cx.new(|cx| {
|
||||
Picker::uniform_list(delegate, window, cx)
|
||||
.initial_width(gpui::rems(34.))
|
||||
.minimum_results_width(gpui::rems(34.))
|
||||
.height(gpui::rems(24.))
|
||||
.no_vertical_padding()
|
||||
});
|
||||
let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx));
|
||||
Self { picker }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,13 +40,7 @@ impl ExtensionVersionSelector {
|
|||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Self {
|
||||
let picker = cx.new(|cx| {
|
||||
Picker::uniform_list(delegate, window, cx)
|
||||
.initial_width(rems(34.))
|
||||
.minimum_results_width(rems(34.))
|
||||
.height(rems(24.))
|
||||
.no_vertical_padding()
|
||||
});
|
||||
let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx));
|
||||
Self { picker }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -276,7 +276,6 @@ pub fn init(cx: &mut App) {
|
|||
workspace.toggle_modal(window, cx, |window, cx| {
|
||||
let delegate = DevExtensionRebuildPickerDelegate::new(dev_extensions);
|
||||
Picker::uniform_list(delegate, window, cx)
|
||||
.minimum_results_width(rems(34.))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -169,20 +169,10 @@ impl FileFinder {
|
|||
let modal_max_width_setting = FileFinderSettings::get_global(cx).modal_max_width;
|
||||
|
||||
let project = delegate.project.clone();
|
||||
let modal_max_width = Self::modal_max_width(modal_max_width_setting, window);
|
||||
let picker = cx.new(|cx| {
|
||||
let picker = Picker::uniform_list_with_preview(delegate, project, window, cx)
|
||||
.minimum_results_width(gpui::rems(34.))
|
||||
.height(gpui::rems(24.))
|
||||
.no_vertical_padding();
|
||||
// The dedicated file finder width setting has been removed in favor of
|
||||
// persisted picker sizes. For migration we still honor it as the initial
|
||||
// width, but only if the user actually changed it from the default;
|
||||
if modal_max_width_setting != FileFinderWidth::default() {
|
||||
let modal_max_width = Self::modal_max_width(modal_max_width_setting, window);
|
||||
picker.initial_width(Rems::from_pixels(modal_max_width, window))
|
||||
} else {
|
||||
picker
|
||||
}
|
||||
Picker::uniform_list_with_preview(delegate, project, window, cx)
|
||||
.initial_width(Rems::from_pixels(modal_max_width, window))
|
||||
});
|
||||
let picker_focus_handle = picker.focus_handle(cx);
|
||||
picker.update(cx, |picker, _| {
|
||||
|
|
|
|||
|
|
@ -277,11 +277,9 @@ impl BranchList {
|
|||
|
||||
let picker = cx.new(|cx| {
|
||||
Picker::uniform_list(delegate, window, cx)
|
||||
.minimum_results_width(width)
|
||||
.height(rems(24.))
|
||||
.no_vertical_padding()
|
||||
.initial_width(width)
|
||||
.show_scrollbar(true)
|
||||
.modal(!embedded)
|
||||
.when(embedded, |picker| picker.embedded())
|
||||
});
|
||||
let picker_focus_handle = picker.focus_handle(cx);
|
||||
|
||||
|
|
|
|||
|
|
@ -111,9 +111,6 @@ impl CommitTagPicker {
|
|||
let picker = cx.new(|cx| {
|
||||
Picker::nonsearchable_uniform_list(delegate, window, cx)
|
||||
.initial_width(COMMIT_TAG_LIST_WIDTH_IN_REMS)
|
||||
.minimum_results_width(COMMIT_TAG_LIST_WIDTH_IN_REMS)
|
||||
.height(rems(24.))
|
||||
.no_vertical_padding()
|
||||
});
|
||||
Self { picker }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,13 +55,8 @@ impl PickerPrompt {
|
|||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Self {
|
||||
let picker = cx.new(|cx| {
|
||||
Picker::uniform_list(delegate, window, cx)
|
||||
.initial_width(rems(rem_width))
|
||||
.minimum_results_width(rems(rem_width))
|
||||
.height(rems(24.))
|
||||
.no_vertical_padding()
|
||||
});
|
||||
let picker =
|
||||
cx.new(|cx| Picker::uniform_list(delegate, window, cx).initial_width(rems(rem_width)));
|
||||
let _subscription = cx.subscribe(&picker, |_, _, _, cx| cx.emit(DismissEvent));
|
||||
Self {
|
||||
picker,
|
||||
|
|
|
|||
|
|
@ -64,9 +64,7 @@ impl RepositorySelector {
|
|||
|
||||
let picker = cx.new(|cx| {
|
||||
Picker::uniform_list(delegate, window, cx)
|
||||
.minimum_results_width(width)
|
||||
.height(rems(20.))
|
||||
.no_vertical_padding()
|
||||
.initial_width(width)
|
||||
.show_scrollbar(true)
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -128,11 +128,9 @@ impl StashList {
|
|||
let delegate = StashListDelegate::new(repository, workspace, window, cx);
|
||||
let picker = cx.new(|cx| {
|
||||
Picker::uniform_list(delegate, window, cx)
|
||||
.minimum_results_width(width)
|
||||
.height(rems(24.))
|
||||
.no_vertical_padding()
|
||||
.initial_width(width)
|
||||
.show_scrollbar(true)
|
||||
.modal(!embedded)
|
||||
.when(embedded, |picker| picker.embedded())
|
||||
});
|
||||
let picker_focus_handle = picker.focus_handle(cx);
|
||||
picker.update(cx, |picker, _| {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use git::repository::Worktree as GitWorktree;
|
|||
use gpui::{
|
||||
Action, AnyElement, App, Context, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable,
|
||||
InteractiveElement, IntoElement, Modifiers, ModifiersChangedEvent, ParentElement, PromptLevel,
|
||||
Render, SharedString, Styled, Subscription, Task, TaskExt, WeakEntity, Window, actions, rems,
|
||||
Render, SharedString, Styled, Subscription, Task, TaskExt, WeakEntity, Window, actions,
|
||||
};
|
||||
use picker::{Picker, PickerDelegate, PickerEditorPosition};
|
||||
use project::Project;
|
||||
|
|
@ -128,11 +128,7 @@ impl WorktreePicker {
|
|||
Picker::list(delegate, window, cx)
|
||||
.list_measure_all()
|
||||
.show_scrollbar(true)
|
||||
.modal(false)
|
||||
.initial_width(rems(34.))
|
||||
.minimum_results_width(rems(34.))
|
||||
.height(rems(20.))
|
||||
.no_vertical_padding()
|
||||
.embedded()
|
||||
});
|
||||
|
||||
let picker_focus_handle = picker.focus_handle(cx);
|
||||
|
|
|
|||
|
|
@ -83,13 +83,7 @@ impl LanguageSelector {
|
|||
current_language_name,
|
||||
);
|
||||
|
||||
let picker = cx.new(|cx| {
|
||||
Picker::uniform_list(delegate, window, cx)
|
||||
.initial_width(rems(34.))
|
||||
.minimum_results_width(rems(34.))
|
||||
.height(rems(24.))
|
||||
.no_vertical_padding()
|
||||
});
|
||||
let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx));
|
||||
Self { picker }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,13 +65,7 @@ impl LineEndingSelector {
|
|||
let line_ending = buffer.read(cx).line_ending();
|
||||
let delegate =
|
||||
LineEndingSelectorDelegate::new(cx.entity().downgrade(), buffer, project, line_ending);
|
||||
let picker = cx.new(|cx| {
|
||||
Picker::nonsearchable_uniform_list(delegate, window, cx)
|
||||
.initial_width(rems(34.))
|
||||
.minimum_results_width(rems(34.))
|
||||
.height(rems(24.))
|
||||
.no_vertical_padding()
|
||||
});
|
||||
let picker = cx.new(|cx| Picker::nonsearchable_uniform_list(delegate, window, cx));
|
||||
Self { picker }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,13 +61,7 @@ impl BaseKeymapSelector {
|
|||
window: &mut Window,
|
||||
cx: &mut Context<BaseKeymapSelector>,
|
||||
) -> Self {
|
||||
let picker = cx.new(|cx| {
|
||||
Picker::uniform_list(delegate, window, cx)
|
||||
.initial_width(rems(34.))
|
||||
.minimum_results_width(rems(34.))
|
||||
.height(rems(24.))
|
||||
.no_vertical_padding()
|
||||
});
|
||||
let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx));
|
||||
Self { picker }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -233,8 +233,7 @@ impl OpenPathPrompt {
|
|||
workspace.toggle_modal(window, cx, |window, cx| {
|
||||
let delegate =
|
||||
OpenPathDelegate::new(tx, lister.clone(), creating_path, cx).show_hidden();
|
||||
let picker =
|
||||
Picker::uniform_list(delegate, window, cx).minimum_results_width(rems(34.));
|
||||
let picker = Picker::uniform_list(delegate, window, cx);
|
||||
let mut query = lister.default_query(cx);
|
||||
if let Some(suggested_name) = suggested_name {
|
||||
query.push_str(&suggested_name);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ use gpui::{AppContext, Entity, TestAppContext, VisualTestContext};
|
|||
use picker::{Picker, PickerDelegate};
|
||||
use project::Project;
|
||||
use serde_json::json;
|
||||
use ui::rems;
|
||||
use util::path;
|
||||
use workspace::{AppState, MultiWorkspace};
|
||||
|
||||
|
|
@ -509,9 +508,7 @@ fn build_open_path_prompt(
|
|||
delegate
|
||||
};
|
||||
cx.new(|cx| {
|
||||
let picker = Picker::uniform_list(delegate, window, cx)
|
||||
.minimum_results_width(rems(34.))
|
||||
.modal(false);
|
||||
let picker = Picker::uniform_list(delegate, window, cx).embedded();
|
||||
let query = lister.default_query(cx);
|
||||
picker.set_query(&query, window, cx);
|
||||
picker
|
||||
|
|
|
|||
|
|
@ -179,12 +179,10 @@ impl OutlineView {
|
|||
let delegate = OutlineViewDelegate::new(cx.entity().downgrade(), outline, editor, cx);
|
||||
let picker = cx.new(|cx| {
|
||||
Picker::uniform_list(delegate, window, cx)
|
||||
.minimum_results_width(rems(34.))
|
||||
.height(Rems::from_pixels(
|
||||
.max_height(Rems::from_pixels(
|
||||
window.viewport_size().height * 0.75,
|
||||
window,
|
||||
))
|
||||
.no_vertical_padding()
|
||||
.show_scrollbar(true)
|
||||
});
|
||||
OutlineView { picker }
|
||||
|
|
|
|||
|
|
@ -127,14 +127,12 @@ impl<D: PickerDelegate> Picker<D> {
|
|||
cx: &mut Context<Self>,
|
||||
) -> impl IntoElement {
|
||||
let focus_handle = self.focus_handle(cx);
|
||||
let toggle_focus_handle = focus_handle.clone();
|
||||
let right_focus_handle = focus_handle.clone();
|
||||
let below_focus_handle = focus_handle.clone();
|
||||
let current = self.preview_layout().unwrap_or(preview::Layout::Hidden);
|
||||
let preview_visible = current != preview::Layout::Hidden;
|
||||
|
||||
h_flex()
|
||||
.gap_1()
|
||||
.child(
|
||||
Button::new("picker-preview-toggle", "Preview")
|
||||
.when(preview_visible, |this| this.color(Color::Accent))
|
||||
|
|
@ -142,22 +140,12 @@ impl<D: PickerDelegate> Picker<D> {
|
|||
KeyBinding::for_action_in(&TogglePreview, &focus_handle, cx)
|
||||
.size(rems_from_px(12.)),
|
||||
)
|
||||
.tooltip(move |_window, cx| {
|
||||
Tooltip::for_action_in(
|
||||
"Toggle Preview",
|
||||
&TogglePreview,
|
||||
&toggle_focus_handle,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.on_click(
|
||||
cx.listener(|this, _, window, cx| this.toggle_preview_visible(window, cx)),
|
||||
),
|
||||
)
|
||||
// The layout buttons (preview to the right / below) are only relevant
|
||||
// once the preview is showing, so hide them while it's hidden.
|
||||
.when(preview_visible, |this| {
|
||||
this.child(div().child(Divider::vertical().color(ui::DividerColor::Border)))
|
||||
this.child(Divider::vertical().h_4().mx_1())
|
||||
.child(
|
||||
IconButton::new("picker-preview-right", IconName::DiffSplit)
|
||||
.icon_size(IconSize::Small)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use ui::Window;
|
|||
use crate::preview;
|
||||
use crate::shape::{self, Centered, RelativeHeight, RelativeWidth, Shape, ViewportFraction};
|
||||
|
||||
const PICKERS_NAMESPACE: &str = "pickers";
|
||||
const PICKERS_NAMESPACE: &str = "pickers_v2";
|
||||
|
||||
pub(crate) fn store_shape_for_this_layout(
|
||||
picker_delegate: &'static str,
|
||||
|
|
|
|||
|
|
@ -38,6 +38,9 @@ pub use preview::PreviewSource;
|
|||
pub use preview::Update as PreviewUpdate;
|
||||
pub use ui_input::ErasedEditor;
|
||||
|
||||
pub const DEFAULT_MODAL_WIDTH: Rems = Rems(34.0);
|
||||
pub const DEFAULT_MODAL_MAX_HEIGHT: Rems = Rems(24.0);
|
||||
|
||||
enum ElementContainer {
|
||||
List(ListState),
|
||||
UniformList(UniformListScrollHandle),
|
||||
|
|
@ -88,6 +91,30 @@ struct PendingUpdateMatches {
|
|||
_task: Task<Result<()>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum Presentation {
|
||||
/// A self-contained modal: draws its own elevated background and dismisses
|
||||
/// when it loses focus. May optionally be resized (persisting its size);
|
||||
/// resizing only makes sense for modals.
|
||||
Modal { resizable: bool },
|
||||
/// A popover attached to a menu/trigger: draws its own elevated background
|
||||
/// and dismisses when it loses focus, but is never resizable.
|
||||
Popover,
|
||||
/// Embedded inside a larger container (e.g. another modal) that provides its
|
||||
/// own chrome and handles dismissal.
|
||||
Embedded,
|
||||
}
|
||||
|
||||
/// The default size for a given preview layout. With the preview hidden the
|
||||
/// picker uses its standard size; showing a preview expands it to the larger
|
||||
/// "telescope" size so the results pane isn't cramped beside the preview.
|
||||
fn default_shape_for_layout(hidden: shape::Centered, layout: preview::Layout) -> shape::Centered {
|
||||
match layout {
|
||||
preview::Layout::Hidden => hidden,
|
||||
preview::Layout::Right | preview::Layout::Below => shape::Centered::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Picker<D: PickerDelegate> {
|
||||
pub delegate: D,
|
||||
element_container: ElementContainer,
|
||||
|
|
@ -96,16 +123,14 @@ pub struct Picker<D: PickerDelegate> {
|
|||
pending_update_matches: Option<PendingUpdateMatches>,
|
||||
confirm_on_update: Option<bool>,
|
||||
shape: shape::Shape,
|
||||
/// set through [Picker::width] and [Picker::height]
|
||||
/// The size the picker opens at (and resets to). Defaults depend on whether
|
||||
/// the picker has a preview; see [`Picker::initial_width`] / [`Picker::max_height`].
|
||||
default_shape: shape::Centered,
|
||||
vertical_padding: shape::VerticalPadding,
|
||||
size_bounds: shape::SizeBounds,
|
||||
/// An external control to display a scrollbar in the `Picker`.
|
||||
show_scrollbar: bool,
|
||||
/// Whether the `Picker` is rendered as a self-contained modal.
|
||||
///
|
||||
/// Set this to `false` when rendering the `Picker` as part of a larger modal.
|
||||
is_modal: bool,
|
||||
/// How the picker is presented, which controls its chrome and behavior.
|
||||
presentation: Presentation,
|
||||
/// Bounds tracking for the picker container (for aside positioning)
|
||||
picker_bounds: Rc<Cell<Option<Bounds<Pixels>>>>,
|
||||
/// Bounds tracking for items (for aside positioning) - maps item index to bounds
|
||||
|
|
@ -474,9 +499,27 @@ impl<D: PickerDelegate> Picker<D> {
|
|||
.flatten()
|
||||
.unwrap_or_default();
|
||||
};
|
||||
let shape = persistence::try_load_shape(D::name(), preview.as_ref().map(|p| p.layout), cx)
|
||||
.log_err()
|
||||
.flatten();
|
||||
let has_preview = preview.is_some();
|
||||
let persisted_shape =
|
||||
persistence::try_load_shape(D::name(), preview.as_ref().map(|p| p.layout), cx)
|
||||
.log_err()
|
||||
.flatten();
|
||||
// Every picker opens at the standard "simple" size: a fixed width and a
|
||||
// standard max height it shrinks below when there's little content.
|
||||
// Showing a preview expands it to the larger "telescope" size (see
|
||||
// `default_shape_for_layout`).
|
||||
let default_shape = shape::Centered::simple();
|
||||
let initial_layout = preview
|
||||
.as_ref()
|
||||
.map(|p| p.layout)
|
||||
.unwrap_or(preview::Layout::Hidden);
|
||||
let mut size_bounds = shape::SizeBounds::default();
|
||||
// For a plain picker the whole-picker minimum is just its opening width,
|
||||
// so it can't be resized/clamped narrower than it opens. Preview pickers
|
||||
// keep the standard per-pane minimums.
|
||||
if !has_preview && let Some(width) = default_shape.width.as_rems() {
|
||||
size_bounds.min_results.width = width;
|
||||
}
|
||||
let mut this = Self {
|
||||
delegate,
|
||||
head,
|
||||
|
|
@ -484,15 +527,21 @@ impl<D: PickerDelegate> Picker<D> {
|
|||
pending_update_matches: None,
|
||||
confirm_on_update: None,
|
||||
preview,
|
||||
shape_loaded_from_persistence: shape.is_some(),
|
||||
shape: shape.unwrap_or_default(),
|
||||
default_shape: shape::Centered::default(),
|
||||
vertical_padding: shape::VerticalPadding::default(),
|
||||
shape_loaded_from_persistence: persisted_shape.is_some(),
|
||||
shape: persisted_shape.unwrap_or_else(|| {
|
||||
shape::Shape::HorizontallyCentered(default_shape_for_layout(
|
||||
default_shape,
|
||||
initial_layout,
|
||||
))
|
||||
}),
|
||||
default_shape,
|
||||
show_scrollbar: false,
|
||||
is_modal: true,
|
||||
presentation: Presentation::Modal {
|
||||
resizable: has_preview,
|
||||
},
|
||||
picker_bounds: Rc::new(Cell::new(None)),
|
||||
item_bounds: Rc::new(RefCell::new(HashMap::default())),
|
||||
size_bounds: shape::SizeBounds::default(),
|
||||
size_bounds,
|
||||
actions_menu_handle: PopoverMenuHandle::default(),
|
||||
};
|
||||
this.update_matches("".to_string(), window, cx);
|
||||
|
|
@ -513,34 +562,33 @@ impl<D: PickerDelegate> Picker<D> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Sets the width the picker appears with if the user has never resized it
|
||||
/// or when the user sets it back to it's default size.
|
||||
/// Overrides the width the picker opens at (and resets to). Plain pickers
|
||||
/// default to [`DEFAULT_MODAL_WIDTH`]; only call this for pickers that need a
|
||||
/// different width (e.g. narrow popover selectors).
|
||||
///
|
||||
/// For a plain (no-preview) picker the opening width is also its minimum, so
|
||||
/// it can't be resized or clamped narrower than it opens.
|
||||
pub fn initial_width(mut self, width: impl Into<RelativeWidth>) -> Self {
|
||||
let width = width.into();
|
||||
self.default_shape.width = width;
|
||||
if !self.shape_loaded_from_persistence {
|
||||
self.shape.set_initial_width(width);
|
||||
}
|
||||
// A plain picker's whole-picker minimum tracks its opening width. Preview
|
||||
// pickers keep the standard per-pane minimums.
|
||||
if self.preview.is_none()
|
||||
&& let Some(rems) = width.as_rems()
|
||||
{
|
||||
self.size_bounds.min_results.width = rems;
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the minimum width, the picker can not be resized smaller then this.
|
||||
/// Leave unset to use sane defaults.
|
||||
///
|
||||
/// This applies to the results. If there is no preview that is the whole picker.
|
||||
pub fn minimum_results_width(mut self, width: impl Into<Rems>) -> Self {
|
||||
self.size_bounds.min_results.width = width.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the width the picker appears with if the user has never resized it
|
||||
/// or when the user sets it back to it's default size.
|
||||
///
|
||||
/// # Padding
|
||||
/// By default the picker will fill this space. If you want it to only grow
|
||||
/// as large as it needs and treat the height as a bound use
|
||||
/// [`no_vertical_padding`]
|
||||
pub fn height(mut self, height: impl Into<RelativeHeight>) -> Self {
|
||||
/// Overrides the picker's max height. Plain pickers default to
|
||||
/// [`DEFAULT_MODAL_MAX_HEIGHT`] and shrink below it to fit their content;
|
||||
/// only call this for pickers that want a different cap (e.g. the outline
|
||||
/// view, which is taller).
|
||||
pub fn max_height(mut self, height: impl Into<RelativeHeight>) -> Self {
|
||||
let height = height.into();
|
||||
self.default_shape.height = height;
|
||||
if !self.shape_loaded_from_persistence {
|
||||
|
|
@ -549,23 +597,12 @@ impl<D: PickerDelegate> Picker<D> {
|
|||
self
|
||||
}
|
||||
|
||||
/// Makes the picker shrink to fit its content rather than padding out to its
|
||||
/// full height when there are fewer results than fit.
|
||||
pub fn no_vertical_padding(mut self) -> Self {
|
||||
self.vertical_padding = shape::VerticalPadding::None;
|
||||
self
|
||||
}
|
||||
|
||||
fn vertical_padding(&self) -> shape::VerticalPadding {
|
||||
let preview_visible = self
|
||||
.preview
|
||||
/// Whether the picker fills its full height (preview visible) or shrinks to
|
||||
/// fit its content, treating the height as a maximum (no preview visible).
|
||||
fn fill_height(&self) -> bool {
|
||||
self.preview
|
||||
.as_ref()
|
||||
.is_some_and(|preview| preview.layout != preview::Layout::Hidden);
|
||||
if preview_visible {
|
||||
shape::VerticalPadding::Pad
|
||||
} else {
|
||||
self.vertical_padding
|
||||
}
|
||||
.is_some_and(|preview| preview.layout != preview::Layout::Hidden)
|
||||
}
|
||||
|
||||
pub fn show_scrollbar(mut self, show_scrollbar: bool) -> Self {
|
||||
|
|
@ -573,11 +610,50 @@ impl<D: PickerDelegate> Picker<D> {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn modal(mut self, modal: bool) -> Self {
|
||||
self.is_modal = modal;
|
||||
/// Presents the picker as embedded inside a larger container that provides
|
||||
/// its own chrome and dismissal, rather than the default self-contained
|
||||
/// modal. Use [`popover`](Self::popover) instead for menu-attached pickers.
|
||||
pub fn embedded(mut self) -> Self {
|
||||
self.presentation = Presentation::Embedded;
|
||||
self
|
||||
}
|
||||
|
||||
/// Presents the picker as a popover surface: it draws its own elevated
|
||||
/// background (like a modal) but is not resizable. Use this for pickers
|
||||
/// shown inside a menu/popover.
|
||||
pub fn popover(mut self) -> Self {
|
||||
self.set_popover();
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn set_popover(&mut self) {
|
||||
self.presentation = Presentation::Popover;
|
||||
}
|
||||
|
||||
/// Controls whether the user can drag to resize the picker (and whether its
|
||||
/// size is persisted). Only applies to modal pickers; no-op otherwise.
|
||||
/// Defaults to `true` for pickers with a preview and `false` otherwise.
|
||||
pub fn resizable(mut self, resizable: bool) -> Self {
|
||||
if let Presentation::Modal { resizable: r } = &mut self.presentation {
|
||||
*r = resizable;
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Whether the picker draws its own elevated background and dismisses on
|
||||
/// blur (modals and popovers, but not embedded pickers).
|
||||
fn draws_own_container(&self) -> bool {
|
||||
matches!(
|
||||
self.presentation,
|
||||
Presentation::Modal { .. } | Presentation::Popover
|
||||
)
|
||||
}
|
||||
|
||||
/// Whether the picker can be resized (only ever true for modals).
|
||||
fn is_resizable(&self) -> bool {
|
||||
matches!(self.presentation, Presentation::Modal { resizable: true })
|
||||
}
|
||||
|
||||
pub fn list_measure_all(mut self) -> Self {
|
||||
match self.element_container {
|
||||
ElementContainer::List(state) => {
|
||||
|
|
@ -888,7 +964,7 @@ impl<D: PickerDelegate> Picker<D> {
|
|||
let menu_focused = self.actions_menu_handle.is_focused(window, cx)
|
||||
|| self.actions_menu_handle.is_deployed()
|
||||
|| self.delegate.has_another_open_menu(window, cx);
|
||||
if self.is_modal && window.is_window_active() && !menu_focused {
|
||||
if self.draws_own_container() && window.is_window_active() && !menu_focused {
|
||||
self.cancel(&menu::Cancel, window, cx);
|
||||
}
|
||||
}
|
||||
|
|
@ -1107,12 +1183,13 @@ impl<D: PickerDelegate> Picker<D> {
|
|||
}
|
||||
|
||||
fn render_element_container(&self, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
// When the picker shrinks to fit content (`None`), the list infers its
|
||||
// size from its items. When the picker pads to its full height (`Pad`),
|
||||
// the list fills the available space.
|
||||
let sizing_behavior = match self.vertical_padding() {
|
||||
shape::VerticalPadding::None => ListSizingBehavior::Infer,
|
||||
shape::VerticalPadding::Pad => ListSizingBehavior::Auto,
|
||||
// When the picker shrinks to fit its content, the list infers its size
|
||||
// from its items. When it fills its full height (preview visible), the
|
||||
// list fills the available space.
|
||||
let sizing_behavior = if self.fill_height() {
|
||||
ListSizingBehavior::Auto
|
||||
} else {
|
||||
ListSizingBehavior::Infer
|
||||
};
|
||||
|
||||
match &self.element_container {
|
||||
|
|
@ -1179,12 +1256,17 @@ impl<D: PickerDelegate> Picker<D> {
|
|||
return;
|
||||
};
|
||||
preview.layout = layout;
|
||||
if let Some(previously_resized) = persistence::try_load_shape(D::name(), layout, cx)
|
||||
// Restore the size the user last left this layout at, or fall back to the
|
||||
// layout's default (simple when hidden, larger when a preview is shown).
|
||||
self.shape = persistence::try_load_shape(D::name(), layout, cx)
|
||||
.log_err()
|
||||
.flatten()
|
||||
{
|
||||
self.shape = previously_resized;
|
||||
}
|
||||
.unwrap_or_else(|| {
|
||||
shape::Shape::HorizontallyCentered(default_shape_for_layout(
|
||||
self.default_shape,
|
||||
layout,
|
||||
))
|
||||
});
|
||||
self.delegate
|
||||
.preview_layout_changed(matches!(layout, preview::Layout::Right));
|
||||
cx.notify();
|
||||
|
|
@ -1366,3 +1448,4 @@ mod tests {
|
|||
|
||||
impl<D: PickerDelegate> EventEmitter<DismissEvent> for Picker<D> {}
|
||||
impl<D: PickerDelegate> ModalView for Picker<D> {}
|
||||
impl<D: PickerDelegate> ui::FluentBuilder for Picker<D> {}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ where
|
|||
anchor: Anchor,
|
||||
cx: &mut App,
|
||||
) -> Self {
|
||||
picker.update(cx, |picker, _| picker.is_modal = false);
|
||||
picker.update(cx, |picker, _| picker.set_popover());
|
||||
Self {
|
||||
_subscriptions: vec![cx.subscribe(&picker, |picker, &DismissEvent, cx| {
|
||||
picker.update(cx, |_, cx| cx.emit(DismissEvent));
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ impl EditorPreview {
|
|||
if let Some(message) = &self.message {
|
||||
self.render_message(message, cx).into_any_element()
|
||||
} else {
|
||||
debug_assert!(!self.preview_editor.read(cx).is_empty(cx));
|
||||
div()
|
||||
.flex_1()
|
||||
.overflow_hidden()
|
||||
|
|
|
|||
|
|
@ -54,22 +54,23 @@ impl<D: PickerDelegate> Render for Picker<D> {
|
|||
// off.
|
||||
let has_preview = self.preview.is_some();
|
||||
let content = div()
|
||||
.when(self.is_modal, |this| this.elevation_3(cx))
|
||||
.when(self.draws_own_container(), |this| this.elevation_3(cx))
|
||||
.when(has_preview, |this| this.overflow_hidden())
|
||||
.child(content);
|
||||
|
||||
let layout = self.preview_layout().unwrap_or(Layout::Hidden);
|
||||
|
||||
// Embedded pickers are not resizable
|
||||
div().relative().child(content).when(self.is_modal, |this| {
|
||||
// While resizing offset the (parent-centered) container
|
||||
this.left(self.shape.horizontal_offset(window))
|
||||
.child(self.render_resize(Left, window, cx))
|
||||
.child(self.render_resize(Right(layout), window, cx))
|
||||
.child(self.render_resize(Bottom(layout), window, cx))
|
||||
.child(self.render_resize(LeftCorner(layout), window, cx))
|
||||
.child(self.render_resize(RightCorner(layout), window, cx))
|
||||
})
|
||||
div()
|
||||
.relative()
|
||||
.child(content)
|
||||
.when(self.is_resizable(), |this| {
|
||||
this.left(self.shape.horizontal_offset(window))
|
||||
.child(self.render_resize(Left, window, cx))
|
||||
.child(self.render_resize(Right(layout), window, cx))
|
||||
.child(self.render_resize(Bottom(layout), window, cx))
|
||||
.child(self.render_resize(LeftCorner(layout), window, cx))
|
||||
.child(self.render_resize(RightCorner(layout), window, cx))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -95,7 +96,7 @@ impl<D: PickerDelegate> Picker<D> {
|
|||
self.shape.apply_results_size(
|
||||
self.preview_layout(),
|
||||
&self.size_bounds,
|
||||
self.vertical_padding(),
|
||||
self.fill_height(),
|
||||
this,
|
||||
window,
|
||||
)
|
||||
|
|
@ -154,7 +155,7 @@ impl<D: PickerDelegate> Picker<D> {
|
|||
.when_some(
|
||||
self.shape.results_max_height(
|
||||
&self.size_bounds,
|
||||
self.vertical_padding(),
|
||||
self.fill_height(),
|
||||
window,
|
||||
),
|
||||
|this, max_height| this.max_h(max_height),
|
||||
|
|
@ -300,7 +301,9 @@ impl<D: PickerDelegate> Picker<D> {
|
|||
.child(preview.render(cx)),
|
||||
),
|
||||
)
|
||||
.child(self.render_resize(window_controls::Middle(preview.layout), window, cx))
|
||||
.when(self.is_resizable(), |this| {
|
||||
this.child(self.render_resize(window_controls::Middle(preview.layout), window, cx))
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn render_with_preview_right(
|
||||
|
|
@ -342,7 +345,9 @@ impl<D: PickerDelegate> Picker<D> {
|
|||
.child(preview.render(cx)),
|
||||
),
|
||||
)
|
||||
.child(self.render_resize(Middle(Layout::Right), window, cx))
|
||||
.when(self.is_resizable(), |this| {
|
||||
this.child(self.render_resize(Middle(Layout::Right), window, cx))
|
||||
})
|
||||
}
|
||||
|
||||
fn finish_any_completed_resize(
|
||||
|
|
|
|||
|
|
@ -72,6 +72,13 @@ macro_rules! relative_size {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns this size as [`Rems`] when it has no viewport-relative
|
||||
/// component. Used to derive a rems-based minimum from an initial
|
||||
/// size without needing a [`Window`].
|
||||
pub fn as_rems(&self) -> Option<Rems> {
|
||||
(self.viewport_fraction == 0.0).then_some(self.rems)
|
||||
}
|
||||
|
||||
pub fn as_viewport_fraction(&self, window: &Window) -> ViewportFraction {
|
||||
ViewportFraction(
|
||||
self.viewport_fraction
|
||||
|
|
@ -181,15 +188,6 @@ impl std::ops::Mul<f32> for ViewportFraction {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
pub(crate) enum VerticalPadding {
|
||||
/// The picker always fills its height even if there are no results
|
||||
#[default]
|
||||
Pad,
|
||||
/// Picker might be shorter then it's height if there is not enough to display
|
||||
None,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub(crate) struct Centered {
|
||||
pub(crate) width: RelativeWidth,
|
||||
|
|
@ -197,6 +195,19 @@ pub(crate) struct Centered {
|
|||
pub(crate) preview_size: ViewportFraction,
|
||||
}
|
||||
|
||||
impl Centered {
|
||||
/// The default size for a plain picker (no preview): a fixed standard width
|
||||
/// and a standard *max* height that the picker shrinks below when it has
|
||||
/// little content.
|
||||
pub(crate) fn simple() -> Self {
|
||||
Centered {
|
||||
width: RelativeWidth::rems(crate::DEFAULT_MODAL_WIDTH),
|
||||
height: RelativeHeight::rems(crate::DEFAULT_MODAL_MAX_HEIGHT),
|
||||
preview_size: ViewportFraction::ZERO,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub(crate) enum Shape {
|
||||
Resizing(PositionAndShape),
|
||||
|
|
@ -211,7 +222,6 @@ pub(crate) enum Shape {
|
|||
pub struct SizeBounds {
|
||||
pub(crate) max_width: RelativeWidth,
|
||||
pub(crate) max_height: RelativeHeight,
|
||||
/// Minimum size of the results pane.
|
||||
pub(crate) min_results: Size<Rems>,
|
||||
/// Minimum size of the preview pane. Along the split axis this only needs to
|
||||
/// be large enough to grab the divider and shrink it back.
|
||||
|
|
@ -487,7 +497,7 @@ impl Shape {
|
|||
&self,
|
||||
layout: impl Into<Option<Layout>>,
|
||||
bounds: &SizeBounds,
|
||||
vertical_padding: VerticalPadding,
|
||||
fill_height: bool,
|
||||
div: Div,
|
||||
window: &Window,
|
||||
) -> Div {
|
||||
|
|
@ -500,29 +510,27 @@ impl Shape {
|
|||
_ => full.right - full.left,
|
||||
};
|
||||
let div = div.w(width);
|
||||
match vertical_padding {
|
||||
VerticalPadding::None => div,
|
||||
VerticalPadding::Pad => {
|
||||
let height = match layout {
|
||||
Some(Layout::Below) => (full.bottom - full.top) - full.preview,
|
||||
_ => full.bottom - full.top,
|
||||
};
|
||||
div.h(height)
|
||||
}
|
||||
if fill_height {
|
||||
let height = match layout {
|
||||
Some(Layout::Below) => (full.bottom - full.top) - full.preview,
|
||||
_ => full.bottom - full.top,
|
||||
};
|
||||
div.h(height)
|
||||
} else {
|
||||
div
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn results_max_height(
|
||||
&self,
|
||||
bounds: &SizeBounds,
|
||||
vertical_padding: VerticalPadding,
|
||||
fill_height: bool,
|
||||
window: &Window,
|
||||
) -> Option<Pixels> {
|
||||
match vertical_padding {
|
||||
// No preview when results size themselves (no vertical padding), so
|
||||
// clamp against the results-only minimum.
|
||||
VerticalPadding::None => Some(self.height(None, bounds, window)),
|
||||
VerticalPadding::Pad => None,
|
||||
if fill_height {
|
||||
None
|
||||
} else {
|
||||
Some(self.height(None, bounds, window))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use editor::{Bias, Editor, SelectionEffects, scroll::Autoscroll, styled_runs_for
|
|||
use fuzzy::{StringMatch, StringMatchCandidate};
|
||||
use gpui::{
|
||||
App, Context, DismissEvent, Entity, HighlightStyle, ParentElement, StyledText, Task, TaskExt,
|
||||
TextStyle, WeakEntity, Window, relative, rems,
|
||||
TextStyle, WeakEntity, Window, relative,
|
||||
};
|
||||
use ordered_float::OrderedFloat;
|
||||
use picker::{Picker, PickerDelegate};
|
||||
|
|
@ -26,7 +26,7 @@ pub fn init(cx: &mut App) {
|
|||
let handle = cx.entity().downgrade();
|
||||
workspace.toggle_modal(window, cx, move |window, cx| {
|
||||
let delegate = ProjectSymbolsDelegate::new(handle, project);
|
||||
Picker::uniform_list(delegate, window, cx).minimum_results_width(rems(34.))
|
||||
Picker::uniform_list(delegate, window, cx)
|
||||
})
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -632,9 +632,7 @@ impl RecentProjects {
|
|||
let picker = cx.new(|cx| {
|
||||
Picker::list(delegate, window, cx)
|
||||
.list_measure_all()
|
||||
.minimum_results_width(rems(rem_width))
|
||||
.height(rems(24.))
|
||||
.no_vertical_padding()
|
||||
.initial_width(rems(rem_width))
|
||||
.show_scrollbar(true)
|
||||
});
|
||||
|
||||
|
|
@ -2573,8 +2571,7 @@ mod tests {
|
|||
Picker::list(delegate, window, cx)
|
||||
.list_measure_all()
|
||||
.show_scrollbar(true)
|
||||
.height(Rems::from_pixels(px(240.0), window))
|
||||
.no_vertical_padding()
|
||||
.max_height(Rems::from_pixels(px(240.0), window))
|
||||
});
|
||||
draw(cx);
|
||||
(picker, cx)
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@ impl AddWslDistro {
|
|||
use crate::wsl_picker::{WslDistroSelected, WslPickerDelegate, WslPickerDismissed};
|
||||
|
||||
let delegate = WslPickerDelegate::new();
|
||||
let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx).modal(false));
|
||||
let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx).embedded());
|
||||
|
||||
cx.subscribe_in(
|
||||
&picker,
|
||||
|
|
@ -398,12 +398,7 @@ impl ProjectPicker {
|
|||
let delegate = open_path_prompt::OpenPathDelegate::new(tx, lister, false, cx).show_hidden();
|
||||
|
||||
let picker = cx.new(|cx| {
|
||||
let picker = Picker::uniform_list(delegate, window, cx)
|
||||
.initial_width(rems(34.))
|
||||
.minimum_results_width(rems(34.))
|
||||
.height(rems(24.))
|
||||
.no_vertical_padding()
|
||||
.modal(false);
|
||||
let picker = Picker::uniform_list(delegate, window, cx).embedded();
|
||||
picker.set_query(&home_dir.to_string(), window, cx);
|
||||
picker
|
||||
});
|
||||
|
|
@ -1457,7 +1452,7 @@ impl RemoteServerProjects {
|
|||
if configs.len() > 1 {
|
||||
let delegate = DevContainerPickerDelegate::new(configs, cx.weak_entity());
|
||||
this.dev_container_picker =
|
||||
Some(cx.new(|cx| Picker::uniform_list(delegate, window, cx).modal(false)));
|
||||
Some(cx.new(|cx| Picker::uniform_list(delegate, window, cx).embedded()));
|
||||
} else if let Some(context) = dev_container_context {
|
||||
let config = configs.into_iter().next();
|
||||
this.open_dev_container(config, app_state, context, window, cx);
|
||||
|
|
@ -1506,10 +1501,7 @@ impl RemoteServerProjects {
|
|||
true,
|
||||
cx,
|
||||
);
|
||||
Picker::list(delegate, window, cx)
|
||||
.modal(false)
|
||||
.initial_width(rems(34.))
|
||||
.height(rems(24.))
|
||||
Picker::list(delegate, window, cx).embedded()
|
||||
});
|
||||
let mut read_ssh_config = RemoteSettings::get_global(cx).read_ssh_config;
|
||||
let ssh_config_updates = if read_ssh_config {
|
||||
|
|
@ -2217,7 +2209,7 @@ impl RemoteServerProjects {
|
|||
if configs.len() > 1 {
|
||||
let delegate = DevContainerPickerDelegate::new(configs, cx.weak_entity());
|
||||
self.dev_container_picker =
|
||||
Some(cx.new(|cx| Picker::uniform_list(delegate, window, cx).modal(false)));
|
||||
Some(cx.new(|cx| Picker::uniform_list(delegate, window, cx).embedded()));
|
||||
|
||||
let state =
|
||||
CreateRemoteDevContainer::new(DevContainerCreationProgress::SelectingConfig, cx);
|
||||
|
|
|
|||
|
|
@ -56,8 +56,7 @@ impl SidebarRecentProjects {
|
|||
.list_measure_all()
|
||||
.show_scrollbar(true)
|
||||
.initial_width(rems(18.))
|
||||
.minimum_results_width(rems(18.))
|
||||
.modal(false)
|
||||
.popover()
|
||||
});
|
||||
|
||||
let picker_focus_handle = picker.focus_handle(cx);
|
||||
|
|
|
|||
|
|
@ -203,7 +203,7 @@ impl WslOpenModal {
|
|||
cx: &mut Context<Self>,
|
||||
) -> Self {
|
||||
let delegate = WslPickerDelegate::new();
|
||||
let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx).modal(false));
|
||||
let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx).embedded());
|
||||
|
||||
let selected = cx.subscribe_in(
|
||||
&picker,
|
||||
|
|
|
|||
|
|
@ -481,10 +481,7 @@ where
|
|||
let picker_view = cx.new(|cx| {
|
||||
Picker::list(delegate, window, cx)
|
||||
.list_measure_all()
|
||||
.minimum_results_width(rems(34.))
|
||||
.height(rems(24.))
|
||||
.no_vertical_padding()
|
||||
.modal(false)
|
||||
.popover()
|
||||
});
|
||||
|
||||
PopoverMenu::new("kernel-switcher")
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use picker::Picker;
|
|||
|
||||
use project::ProjectPath;
|
||||
use text::Anchor;
|
||||
use ui::{Rems, Window};
|
||||
use ui::Window;
|
||||
use workspace::{DismissDecision, ModalView, Workspace};
|
||||
|
||||
mod delegate;
|
||||
|
|
@ -223,10 +223,7 @@ impl TextFinder {
|
|||
|
||||
fn new(delegate: Delegate, window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||
let project = delegate.project(cx).clone();
|
||||
let picker = cx.new(|cx| {
|
||||
Picker::list_with_preview(delegate, project, window, cx)
|
||||
.minimum_results_width(Rems(20.0))
|
||||
});
|
||||
let picker = cx.new(|cx| Picker::list_with_preview(delegate, project, window, cx));
|
||||
let picker_weak = picker.downgrade();
|
||||
let picker_focus_handle = picker.focus_handle(cx);
|
||||
picker.update(cx, |picker, cx| {
|
||||
|
|
|
|||
|
|
@ -612,7 +612,7 @@ impl PickerDelegate for Delegate {
|
|||
),
|
||||
picker::PickerAction::separator(),
|
||||
picker::PickerAction::button("Open File", menu::Confirm.boxed_clone()),
|
||||
picker::PickerAction::button("To project search", super::ToProjectSearch.boxed_clone()),
|
||||
picker::PickerAction::button("Open as Tab", super::ToProjectSearch.boxed_clone()),
|
||||
]
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -52,13 +52,8 @@ impl SettingsProfileSelector {
|
|||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Self {
|
||||
let picker = cx.new(|cx| {
|
||||
Picker::uniform_list(delegate, window, cx)
|
||||
.initial_width(rems(22.))
|
||||
.minimum_results_width(rems(22.))
|
||||
.height(rems(24.))
|
||||
.no_vertical_padding()
|
||||
});
|
||||
let picker =
|
||||
cx.new(|cx| Picker::uniform_list(delegate, window, cx).initial_width(rems(22.)));
|
||||
Self { picker }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -180,8 +180,7 @@ pub fn font_picker(
|
|||
|
||||
Picker::uniform_list(delegate, window, cx)
|
||||
.show_scrollbar(true)
|
||||
.minimum_results_width(rems_from_px(210.))
|
||||
.height(rems(18.))
|
||||
.no_vertical_padding()
|
||||
.modal(false)
|
||||
.initial_width(rems_from_px(210.))
|
||||
.max_height(rems(18.))
|
||||
.popover()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -193,8 +193,7 @@ pub fn icon_theme_picker(
|
|||
|
||||
Picker::uniform_list(delegate, window, cx)
|
||||
.show_scrollbar(true)
|
||||
.minimum_results_width(rems_from_px(210.))
|
||||
.height(rems(18.))
|
||||
.no_vertical_padding()
|
||||
.modal(false)
|
||||
.initial_width(rems_from_px(210.))
|
||||
.max_height(rems(18.))
|
||||
.popover()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -204,10 +204,9 @@ pub fn render_ollama_model_picker(
|
|||
|
||||
Picker::uniform_list(delegate, window, cx)
|
||||
.show_scrollbar(true)
|
||||
.minimum_results_width(rems_from_px(210.))
|
||||
.height(rems(18.))
|
||||
.no_vertical_padding()
|
||||
.modal(false)
|
||||
.initial_width(rems_from_px(210.))
|
||||
.max_height(rems(18.))
|
||||
.popover()
|
||||
}))
|
||||
})
|
||||
.anchor(gpui::Anchor::TopLeft)
|
||||
|
|
|
|||
|
|
@ -178,8 +178,7 @@ pub fn theme_picker(
|
|||
|
||||
Picker::uniform_list(delegate, window, cx)
|
||||
.show_scrollbar(true)
|
||||
.minimum_results_width(rems_from_px(210.))
|
||||
.height(rems(18.))
|
||||
.no_vertical_padding()
|
||||
.modal(false)
|
||||
.initial_width(rems_from_px(210.))
|
||||
.max_height(rems(18.))
|
||||
.popover()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -111,13 +111,7 @@ impl ScopeSelector {
|
|||
let delegate =
|
||||
ScopeSelectorDelegate::new(workspace, cx.entity().downgrade(), language_registry);
|
||||
|
||||
let picker = cx.new(|cx| {
|
||||
Picker::uniform_list(delegate, window, cx)
|
||||
.initial_width(rems(34.))
|
||||
.minimum_results_width(rems(34.))
|
||||
.height(rems(24.))
|
||||
.no_vertical_padding()
|
||||
});
|
||||
let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx));
|
||||
|
||||
Self { picker }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -177,7 +177,6 @@ impl TabSwitcher {
|
|||
Picker::nonsearchable_list(delegate, window, cx)
|
||||
}
|
||||
.initial_width(rems(PANEL_WIDTH_REMS))
|
||||
.minimum_results_width(rems(PANEL_WIDTH_REMS))
|
||||
}),
|
||||
init_modifiers,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use fuzzy::{StringMatch, StringMatchCandidate};
|
|||
use gpui::{
|
||||
Action, AnyElement, App, AppContext as _, Context, DismissEvent, Entity, EventEmitter,
|
||||
Focusable, InteractiveElement, ParentElement, Render, Styled, Subscription, Task, WeakEntity,
|
||||
Window, rems,
|
||||
Window,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use picker::{Picker, PickerDelegate, highlighted_match_with_paths::HighlightedMatch};
|
||||
|
|
@ -148,11 +148,7 @@ impl TasksModal {
|
|||
window,
|
||||
cx,
|
||||
)
|
||||
.modal(is_modal)
|
||||
.initial_width(rems(34.))
|
||||
.minimum_results_width(rems(34.))
|
||||
.height(rems(24.))
|
||||
.no_vertical_padding()
|
||||
.when(!is_modal, |picker| picker.embedded())
|
||||
});
|
||||
let mut _subscriptions = [
|
||||
cx.subscribe(&picker, |_, _, _: &DismissEvent, cx| {
|
||||
|
|
|
|||
|
|
@ -45,11 +45,7 @@ impl IconThemeSelector {
|
|||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Self {
|
||||
let picker = cx.new(|cx| {
|
||||
Picker::uniform_list(delegate, window, cx)
|
||||
.initial_width(rems(34.))
|
||||
.minimum_results_width(rems(34.))
|
||||
});
|
||||
let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx));
|
||||
Self { picker }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -119,11 +119,7 @@ impl ThemeSelector {
|
|||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Self {
|
||||
let picker = cx.new(|cx| {
|
||||
Picker::uniform_list(delegate, window, cx)
|
||||
.initial_width(rems(34.))
|
||||
.minimum_results_width(rems(34.))
|
||||
});
|
||||
let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx));
|
||||
Self { picker }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,9 +112,7 @@ impl AddToolchainState {
|
|||
let (lister, rx) = Self::create_path_browser_delegate(project.clone(), cx);
|
||||
let path_style = project.read(cx).path_style(cx);
|
||||
let picker = cx.new(|cx| {
|
||||
let picker = Picker::uniform_list(lister, window, cx)
|
||||
.initial_width(rems(34.))
|
||||
.minimum_results_width(rems(34.));
|
||||
let picker = Picker::uniform_list(lister, window, cx);
|
||||
let mut worktree_root = worktree_root_path.to_string_lossy().into_owned();
|
||||
worktree_root.push_str(path_style.primary_separator());
|
||||
picker.set_query(&worktree_root, window, cx);
|
||||
|
|
@ -234,9 +232,7 @@ impl AddToolchainState {
|
|||
let (delegate, rx) =
|
||||
Self::create_path_browser_delegate(this.project.clone(), cx);
|
||||
picker.update(cx, |picker, cx| {
|
||||
*picker = Picker::uniform_list(delegate, window, cx)
|
||||
.initial_width(rems(34.))
|
||||
.minimum_results_width(rems(34.));
|
||||
*picker = Picker::uniform_list(delegate, window, cx);
|
||||
picker.set_query(path.to_string_lossy().as_ref(), window, cx);
|
||||
});
|
||||
*input_state = Self::wait_for_path(rx, window, cx);
|
||||
|
|
@ -688,8 +684,6 @@ impl ToolchainSelector {
|
|||
cx,
|
||||
);
|
||||
Picker::uniform_list(delegate, window, cx)
|
||||
.initial_width(rems(34.))
|
||||
.minimum_results_width(rems(34.))
|
||||
});
|
||||
let picker_focus_handle = picker.focus_handle(cx);
|
||||
picker.update(cx, |picker, _| {
|
||||
|
|
|
|||
|
|
@ -1433,9 +1433,7 @@ impl RegistersView {
|
|||
matches,
|
||||
};
|
||||
|
||||
Picker::nonsearchable_uniform_list(delegate, window, cx)
|
||||
.initial_width(rems(36.))
|
||||
.modal(true)
|
||||
Picker::nonsearchable_uniform_list(delegate, window, cx).initial_width(rems(36.))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1800,9 +1798,7 @@ impl MarksView {
|
|||
matches,
|
||||
workspace,
|
||||
};
|
||||
Picker::nonsearchable_uniform_list(delegate, window, cx)
|
||||
.initial_width(rems(36.))
|
||||
.modal(true)
|
||||
Picker::nonsearchable_uniform_list(delegate, window, cx).initial_width(rems(36.))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue