chore: audit components
This commit is contained in:
+240
-190
@@ -5,7 +5,7 @@ use gpui::{
|
||||
App, AppContext as _, Axis, Bounds, Context, Entity, FontWeight, InteractiveElement as _,
|
||||
IntoElement, KeyBinding, ListAlignment, ListState, ParentElement as _, Pixels, Render,
|
||||
SharedString, Styled as _, UniformListScrollHandle, WeakEntity, Window, WindowBounds,
|
||||
WindowOptions, actions, div, list, px, size, uniform_list,
|
||||
WindowOptions, actions, div, list, px, rems, size, uniform_list,
|
||||
};
|
||||
use gpui_native_core::alert::{Alert, AlertSeverity, Alerts, AlertsView};
|
||||
use gpui_native_core::avatar::Avatar;
|
||||
@@ -53,9 +53,10 @@ use gpui_native_layout::resizable::{Resizable, ResizableState};
|
||||
use gpui_native_layout::resize_handle::ResizeHandle;
|
||||
use gpui_native_layout::shell::{LayoutState, Shell};
|
||||
use gpui_native_theme::{
|
||||
ActiveTheme as _, Theme, ThemeVariant, active_preset, mode_override, set_mode_override,
|
||||
set_preset,
|
||||
ActiveTheme as _, CODE_OVERSCROLL_MAX, Theme, ThemeVariant, active_preset, mode_override,
|
||||
set_mode_override, set_preset,
|
||||
};
|
||||
use gpui_native_utils::ResultLogErr as _;
|
||||
use gpui_platform::application;
|
||||
|
||||
/// The component catalog: (group, [(panel id, sidebar label)]).
|
||||
@@ -225,6 +226,7 @@ struct Gallery {
|
||||
alert_open: bool,
|
||||
/// Whether the app sidebar paints the window's native material.
|
||||
sidebar_transparent: bool,
|
||||
_subscriptions: Vec<gpui::Subscription>,
|
||||
}
|
||||
|
||||
impl Gallery {
|
||||
@@ -263,7 +265,11 @@ impl Gallery {
|
||||
.iter()
|
||||
.flat_map(|(group, panels)| {
|
||||
let mut entries = vec![SidebarEntry::header(*group)];
|
||||
entries.extend(panels.iter().map(|(_, label)| SidebarEntry::row(*label)));
|
||||
entries.extend(
|
||||
panels
|
||||
.iter()
|
||||
.map(|(id, label)| SidebarEntry::row(*id, *label)),
|
||||
);
|
||||
entries
|
||||
})
|
||||
.collect()
|
||||
@@ -302,7 +308,7 @@ impl Gallery {
|
||||
gallery.dialog_open = open;
|
||||
cx.notify();
|
||||
})
|
||||
.ok();
|
||||
.log_err();
|
||||
})
|
||||
.content(|_, _| {
|
||||
div()
|
||||
@@ -322,7 +328,7 @@ impl Gallery {
|
||||
gallery.dialog_open = false;
|
||||
cx.notify();
|
||||
})
|
||||
.ok();
|
||||
.log_err();
|
||||
})
|
||||
.into_any_element(),
|
||||
Button::new("gallery-dialog-delete")
|
||||
@@ -334,7 +340,7 @@ impl Gallery {
|
||||
gallery.dialog_open = false;
|
||||
cx.notify();
|
||||
})
|
||||
.ok();
|
||||
.log_err();
|
||||
})
|
||||
.into_any_element(),
|
||||
]
|
||||
@@ -353,7 +359,7 @@ impl Gallery {
|
||||
gallery.alert_open = open;
|
||||
cx.notify();
|
||||
})
|
||||
.ok();
|
||||
.log_err();
|
||||
})
|
||||
.content(|_, _| {
|
||||
div()
|
||||
@@ -373,7 +379,7 @@ impl Gallery {
|
||||
gallery.alert_open = false;
|
||||
cx.notify();
|
||||
})
|
||||
.ok();
|
||||
.log_err();
|
||||
})
|
||||
.into_any_element(),
|
||||
Button::new("gallery-alert-signout")
|
||||
@@ -385,7 +391,7 @@ impl Gallery {
|
||||
gallery.alert_open = false;
|
||||
cx.notify();
|
||||
})
|
||||
.ok();
|
||||
.log_err();
|
||||
})
|
||||
.into_any_element(),
|
||||
]
|
||||
@@ -612,7 +618,12 @@ impl Gallery {
|
||||
dock.register_panel(PanelDescriptor::new(
|
||||
"resize-handle",
|
||||
"Resize handle",
|
||||
|_, cx| cx.new(|_| ResizeDemo { width: px(200.) }).into(),
|
||||
|window, cx| {
|
||||
cx.new(|_| ResizeDemo {
|
||||
width: rems(ResizeDemo::WIDTH_DEFAULT_REMS).to_pixels(window.rem_size()),
|
||||
})
|
||||
.into()
|
||||
},
|
||||
));
|
||||
dock.register_panel(PanelDescriptor::new("resizable", "Resizable", |_, cx| {
|
||||
cx.new(ResizableDemo::new).into()
|
||||
@@ -677,7 +688,7 @@ impl Render for WelcomePanel {
|
||||
.child(
|
||||
v_flex()
|
||||
.gap_4()
|
||||
.max_w(px(560.))
|
||||
.max_w(rems(35.))
|
||||
.child(
|
||||
div()
|
||||
.text_lg()
|
||||
@@ -740,7 +751,7 @@ impl Render for WelcomePanel {
|
||||
!gallery.sidebar_transparent;
|
||||
cx.notify();
|
||||
})
|
||||
.ok();
|
||||
.log_err();
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
@@ -789,6 +800,17 @@ struct ButtonDemo {
|
||||
clicks: usize,
|
||||
}
|
||||
|
||||
impl ButtonDemo {
|
||||
fn button_label(variant: ButtonVariant) -> &'static str {
|
||||
match variant {
|
||||
ButtonVariant::Primary => "Primary",
|
||||
ButtonVariant::Secondary => "Secondary",
|
||||
ButtonVariant::Ghost => "Ghost",
|
||||
ButtonVariant::Destructive => "Delete",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for ButtonDemo {
|
||||
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let view = cx.entity();
|
||||
@@ -813,7 +835,7 @@ impl Render for ButtonDemo {
|
||||
let view = view.clone();
|
||||
let mut button =
|
||||
Button::new(SharedString::from(format!("button-demo-{name}")))
|
||||
.label(button_label(variant))
|
||||
.label(Self::button_label(variant))
|
||||
.variant(variant)
|
||||
.on_click(move |_, _, cx| {
|
||||
view.update(cx, |demo, cx| {
|
||||
@@ -839,7 +861,7 @@ impl Render for ButtonDemo {
|
||||
let view = view.clone();
|
||||
let mut button =
|
||||
Button::new(SharedString::from(format!("button-demo-icon-{name}")))
|
||||
.label(button_label(variant))
|
||||
.label(Self::button_label(variant))
|
||||
.icon(IconRole::Folder)
|
||||
.variant(variant)
|
||||
.on_click(move |_, _, cx| {
|
||||
@@ -1174,10 +1196,6 @@ impl Render for StatusDotDemo {
|
||||
}
|
||||
}
|
||||
|
||||
fn combo_option(label: &'static str) -> ComboboxOption<SharedString> {
|
||||
(SharedString::from(label), SharedString::from(label))
|
||||
}
|
||||
|
||||
struct ComboboxDemo {
|
||||
branch: Entity<ComboboxState<SharedString>>,
|
||||
label: Entity<ComboboxState<SharedString>>,
|
||||
@@ -1187,6 +1205,10 @@ struct ComboboxDemo {
|
||||
}
|
||||
|
||||
impl ComboboxDemo {
|
||||
fn combo_option(label: &'static str) -> ComboboxOption<SharedString> {
|
||||
(SharedString::from(label), SharedString::from(label))
|
||||
}
|
||||
|
||||
fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||
let view = cx.entity();
|
||||
let branch = cx.new(|cx| {
|
||||
@@ -1195,10 +1217,10 @@ impl ComboboxDemo {
|
||||
"combo-demo-branch",
|
||||
"Branch",
|
||||
vec![
|
||||
combo_option("main"),
|
||||
combo_option("next"),
|
||||
combo_option("release/1.0"),
|
||||
combo_option("release/2.0"),
|
||||
Self::combo_option("main"),
|
||||
Self::combo_option("next"),
|
||||
Self::combo_option("release/1.0"),
|
||||
Self::combo_option("release/2.0"),
|
||||
],
|
||||
)
|
||||
.selected(SharedString::from("main"))
|
||||
@@ -1206,7 +1228,7 @@ impl ComboboxDemo {
|
||||
.keep_unmatched_text()
|
||||
// Widths are style refinements on the builder; the popup
|
||||
// always matches whatever the combobox ends up being.
|
||||
.w(px(280.)),
|
||||
.w(rems(17.5)),
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
@@ -1215,9 +1237,9 @@ impl ComboboxDemo {
|
||||
"combo-demo-label",
|
||||
"Label",
|
||||
vec![
|
||||
combo_option("bug"),
|
||||
combo_option("docs"),
|
||||
combo_option("enhancement"),
|
||||
Self::combo_option("bug"),
|
||||
Self::combo_option("docs"),
|
||||
Self::combo_option("enhancement"),
|
||||
],
|
||||
)
|
||||
.placeholder("Pick or type a label…")
|
||||
@@ -1245,10 +1267,10 @@ impl ComboboxDemo {
|
||||
"combo-demo-fuzzy",
|
||||
"Module",
|
||||
vec![
|
||||
combo_option("combobox"),
|
||||
combo_option("dock"),
|
||||
combo_option("editor"),
|
||||
combo_option("markdown"),
|
||||
Self::combo_option("combobox"),
|
||||
Self::combo_option("dock"),
|
||||
Self::combo_option("editor"),
|
||||
Self::combo_option("markdown"),
|
||||
],
|
||||
)
|
||||
.placeholder("Filter modules…")
|
||||
@@ -1817,7 +1839,7 @@ impl ResizableDemo {
|
||||
}
|
||||
|
||||
impl Render for ResizableDemo {
|
||||
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let colors = cx.theme().colors;
|
||||
let pane = |text: &'static str| {
|
||||
div().size_full().bg(colors.muted).p_2().child(
|
||||
@@ -1836,8 +1858,8 @@ impl Render for ResizableDemo {
|
||||
colors,
|
||||
Resizable::new("resizable-demo-vertical", self.vertical.clone())
|
||||
.size_full()
|
||||
.h(px(320.))
|
||||
.pane_with_min(pane("Tree"), px(96.))
|
||||
.h(rems(20.))
|
||||
.pane_with_min(pane("Tree"), rems(6.).to_pixels(window.rem_size()))
|
||||
.pane(pane("Editor"))
|
||||
.pane(pane("Terminal")),
|
||||
))
|
||||
@@ -1847,7 +1869,7 @@ impl Render for ResizableDemo {
|
||||
Resizable::new("resizable-demo-horizontal", self.horizontal.clone())
|
||||
.axis(Axis::Horizontal)
|
||||
.size_full()
|
||||
.h(px(120.))
|
||||
.h(rems(7.5))
|
||||
.pane(pane("Primary"))
|
||||
.pane(pane("Inspector")),
|
||||
))
|
||||
@@ -2022,13 +2044,13 @@ impl Render for ListDemo {
|
||||
})
|
||||
.items(|_, _| {
|
||||
vec![
|
||||
ListItem::new("Documents").icon(IconRole::Folder),
|
||||
ListItem::new("Pictures")
|
||||
ListItem::new("Documents", "Documents").icon(IconRole::Folder),
|
||||
ListItem::new("Pictures", "Pictures")
|
||||
.icon(IconRole::Folder)
|
||||
.detail("24 items"),
|
||||
ListItem::new("Music").icon(IconRole::Folder),
|
||||
ListItem::new("Archive").disabled(true),
|
||||
ListItem::new("Trash").detail("emptied weekly"),
|
||||
ListItem::new("Music", "Music").icon(IconRole::Folder),
|
||||
ListItem::new("Archive", "Archive").disabled(true),
|
||||
ListItem::new("Trash", "Trash").detail("emptied weekly"),
|
||||
]
|
||||
}),
|
||||
))
|
||||
@@ -2122,14 +2144,20 @@ impl TreeDemo {
|
||||
nodes: &[GalleryNode],
|
||||
depth: usize,
|
||||
prefix: &[usize],
|
||||
key_prefix: &str,
|
||||
items: &mut Vec<TreeItem>,
|
||||
paths: &mut Vec<Vec<usize>>,
|
||||
) {
|
||||
for (index, node) in nodes.iter().enumerate() {
|
||||
let mut path = prefix.to_vec();
|
||||
path.push(index);
|
||||
// Slash-joined label paths key rows by domain identity, so element
|
||||
// ids survive expansion changing the flat row order.
|
||||
let key: SharedString = if key_prefix.is_empty() {
|
||||
node.label.clone()
|
||||
} else {
|
||||
format!("{key_prefix}/{}", node.label).into()
|
||||
};
|
||||
|
||||
let mut item = TreeItem::new(node.label.clone())
|
||||
let mut item = TreeItem::new(key.clone(), node.label.clone())
|
||||
.depth(depth)
|
||||
.expandable(!node.children.is_empty())
|
||||
.expanded(node.expanded);
|
||||
@@ -2137,10 +2165,13 @@ impl TreeDemo {
|
||||
item = item.icon(role);
|
||||
}
|
||||
items.push(item);
|
||||
|
||||
let mut path = prefix.to_vec();
|
||||
path.push(index);
|
||||
paths.push(path.clone());
|
||||
|
||||
if node.expanded {
|
||||
Self::flatten(&node.children, depth + 1, &path, items, paths);
|
||||
Self::flatten(&node.children, depth + 1, &path, &key, items, paths);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2162,7 +2193,7 @@ impl TreeDemo {
|
||||
fn paths(nodes: &[GalleryNode]) -> Vec<Vec<usize>> {
|
||||
let mut items = Vec::new();
|
||||
let mut paths = Vec::new();
|
||||
Self::flatten(nodes, 0, &[], &mut items, &mut paths);
|
||||
Self::flatten(nodes, 0, &[], "", &mut items, &mut paths);
|
||||
paths
|
||||
}
|
||||
}
|
||||
@@ -2174,7 +2205,7 @@ impl Render for TreeDemo {
|
||||
let colors = cx.theme().colors;
|
||||
let mut items = Vec::new();
|
||||
let mut paths = Vec::new();
|
||||
Self::flatten(&self.nodes, 0, &[], &mut items, &mut paths);
|
||||
Self::flatten(&self.nodes, 0, &[], "", &mut items, &mut paths);
|
||||
|
||||
ScrollArea::new("tree-demo")
|
||||
.size_full()
|
||||
@@ -2519,7 +2550,7 @@ impl Render for DialogDemo {
|
||||
gallery.dialog_open = true;
|
||||
cx.notify();
|
||||
})
|
||||
.ok();
|
||||
.log_err();
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
@@ -2532,7 +2563,7 @@ impl Render for DialogDemo {
|
||||
gallery.alert_open = true;
|
||||
cx.notify();
|
||||
})
|
||||
.ok();
|
||||
.log_err();
|
||||
}),
|
||||
),
|
||||
))
|
||||
@@ -2694,7 +2725,10 @@ impl DropdownMenuDemo {
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn set_status(&mut self, message: impl Into<SharedString>, cx: &mut Context<Self>) {
|
||||
fn set_status<S>(&mut self, message: S, cx: &mut Context<Self>)
|
||||
where
|
||||
S: Into<SharedString>,
|
||||
{
|
||||
self.status = Some(message.into());
|
||||
cx.notify();
|
||||
}
|
||||
@@ -2940,7 +2974,7 @@ impl Render for PopoverDemo {
|
||||
.content(|_, _| {
|
||||
v_flex()
|
||||
.gap_1()
|
||||
.w(px(240.))
|
||||
.w(rems(15.))
|
||||
.p_2()
|
||||
.child(
|
||||
Checkbox::new("popover-demo-telemetry").label("Enable telemetry"),
|
||||
@@ -3013,7 +3047,7 @@ impl Render for EmptyStateDemo {
|
||||
"with action",
|
||||
colors,
|
||||
div()
|
||||
.h(px(180.))
|
||||
.h(rems(11.25))
|
||||
.rounded(radius.overlay)
|
||||
.border_1()
|
||||
.border_color(colors.border)
|
||||
@@ -3033,7 +3067,7 @@ impl Render for EmptyStateDemo {
|
||||
"bare",
|
||||
colors,
|
||||
div()
|
||||
.h(px(120.))
|
||||
.h(rems(7.5))
|
||||
.rounded(radius.overlay)
|
||||
.border_1()
|
||||
.border_color(colors.border)
|
||||
@@ -3062,7 +3096,7 @@ impl Render for ScrollAreaDemo {
|
||||
let wide_rows = (0..12).map(|row| {
|
||||
div()
|
||||
.flex_none()
|
||||
.w(px(760.))
|
||||
.w(rems(47.5))
|
||||
.px_3()
|
||||
.py_1()
|
||||
.border_b_1()
|
||||
@@ -3077,7 +3111,7 @@ impl Render for ScrollAreaDemo {
|
||||
"vertical",
|
||||
colors,
|
||||
div()
|
||||
.h(px(200.))
|
||||
.h(rems(12.5))
|
||||
.rounded(radius.control)
|
||||
.border_1()
|
||||
.border_color(colors.border)
|
||||
@@ -3092,7 +3126,7 @@ impl Render for ScrollAreaDemo {
|
||||
"both axes",
|
||||
colors,
|
||||
div()
|
||||
.h(px(160.))
|
||||
.h(rems(10.))
|
||||
.rounded(radius.control)
|
||||
.border_1()
|
||||
.border_color(colors.border)
|
||||
@@ -3117,10 +3151,12 @@ struct ScrollbarDemo {
|
||||
}
|
||||
|
||||
impl ScrollbarDemo {
|
||||
const ROW_HEIGHT_REMS: [f32; 3] = [1.75, 2.5, 3.25];
|
||||
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
uniform: UniformListScrollHandle::new(),
|
||||
list: ListState::new(64, ListAlignment::Top, px(400.)),
|
||||
list: ListState::new(64, ListAlignment::Top, CODE_OVERSCROLL_MAX),
|
||||
visibility: ScrollbarVisibility::Platform,
|
||||
}
|
||||
}
|
||||
@@ -3159,7 +3195,7 @@ impl Render for ScrollbarDemo {
|
||||
colors,
|
||||
div()
|
||||
.relative()
|
||||
.h(px(240.))
|
||||
.h(rems(15.))
|
||||
.rounded(radius.control)
|
||||
.border_1()
|
||||
.border_color(colors.border)
|
||||
@@ -3169,7 +3205,7 @@ impl Render for ScrollbarDemo {
|
||||
range
|
||||
.map(|row| {
|
||||
div()
|
||||
.h(px(24.))
|
||||
.h(rems(1.5))
|
||||
.px_3()
|
||||
.flex()
|
||||
.items_center()
|
||||
@@ -3190,7 +3226,7 @@ impl Render for ScrollbarDemo {
|
||||
colors,
|
||||
div()
|
||||
.relative()
|
||||
.h(px(240.))
|
||||
.h(rems(15.))
|
||||
.rounded(radius.control)
|
||||
.border_1()
|
||||
.border_color(colors.border)
|
||||
@@ -3203,7 +3239,7 @@ impl Render for ScrollbarDemo {
|
||||
.border_b_1()
|
||||
.border_color(colors.border)
|
||||
.child(format!("Item {row} — {} lines tall", 1 + row % 3))
|
||||
.h(px(28. + (row % 3) as f32 * 12.))
|
||||
.h(rems(Self::ROW_HEIGHT_REMS[row % 3]))
|
||||
.into_any_element()
|
||||
})
|
||||
.h_full()
|
||||
@@ -3284,34 +3320,6 @@ enum CodeSample {
|
||||
/// The line count of the large-file sample.
|
||||
const LARGE_SAMPLE_LINES: usize = 5000;
|
||||
|
||||
/// Generates a deterministic pseudo-Rust file of roughly `lines` lines, so
|
||||
/// scrolling the code view can be exercised on realistic content.
|
||||
fn large_sample(lines: usize) -> String {
|
||||
let mut source = String::with_capacity(lines * 48);
|
||||
let mut index = 0usize;
|
||||
while source.lines().count() < lines {
|
||||
let i = index;
|
||||
source.push_str(&format!(
|
||||
"// Section {i}: generated for the scroll performance test.\n"
|
||||
));
|
||||
source.push_str(&format!("struct Section{i} {{\n"));
|
||||
source.push_str(" id: u64,\n");
|
||||
source.push_str(" label: String,\n");
|
||||
source.push_str(" scores: Vec<f64>,\n");
|
||||
source.push_str("}\n\n");
|
||||
source.push_str(&format!("impl Section{i} {{\n"));
|
||||
source.push_str(" /// A one-line summary of this section.\n");
|
||||
source.push_str(" fn describe(&self) -> String {\n");
|
||||
source.push_str(&format!(
|
||||
" format!(\"section {i}: {{}} at {{:?}}\", self.label, self.scores)\n"
|
||||
));
|
||||
source.push_str(" }\n");
|
||||
source.push_str("}\n\n");
|
||||
index += 1;
|
||||
}
|
||||
source
|
||||
}
|
||||
|
||||
struct CodeViewDemo {
|
||||
code: Entity<CodeViewState>,
|
||||
sample: CodeSample,
|
||||
@@ -3329,10 +3337,38 @@ impl CodeViewDemo {
|
||||
Self {
|
||||
code,
|
||||
sample: CodeSample::Rust,
|
||||
large_source: large_sample(LARGE_SAMPLE_LINES).into(),
|
||||
large_source: Self::large_sample(LARGE_SAMPLE_LINES).into(),
|
||||
_subscription: subscription,
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates a deterministic pseudo-Rust file of roughly `lines` lines, so
|
||||
/// scrolling the code view can be exercised on realistic content.
|
||||
fn large_sample(lines: usize) -> String {
|
||||
let mut source = String::with_capacity(lines * 48);
|
||||
let mut index = 0usize;
|
||||
while source.lines().count() < lines {
|
||||
let i = index;
|
||||
source.push_str(&format!(
|
||||
"// Section {i}: generated for the scroll performance test.\n"
|
||||
));
|
||||
source.push_str(&format!("struct Section{i} {{\n"));
|
||||
source.push_str(" id: u64,\n");
|
||||
source.push_str(" label: String,\n");
|
||||
source.push_str(" scores: Vec<f64>,\n");
|
||||
source.push_str("}\n\n");
|
||||
source.push_str(&format!("impl Section{i} {{\n"));
|
||||
source.push_str(" /// A one-line summary of this section.\n");
|
||||
source.push_str(" fn describe(&self) -> String {\n");
|
||||
source.push_str(&format!(
|
||||
" format!(\"section {i}: {{}} at {{:?}}\", self.label, self.scores)\n"
|
||||
));
|
||||
source.push_str(" }\n");
|
||||
source.push_str("}\n\n");
|
||||
index += 1;
|
||||
}
|
||||
source
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for CodeViewDemo {
|
||||
@@ -3375,7 +3411,7 @@ impl Render for CodeViewDemo {
|
||||
"source",
|
||||
colors,
|
||||
div()
|
||||
.h(px(480.))
|
||||
.h(rems(30.))
|
||||
.rounded(radius.control)
|
||||
.border_1()
|
||||
.border_color(colors.border)
|
||||
@@ -3457,36 +3493,6 @@ enum DiffScenario {
|
||||
/// The line count of each version in the large-diff sample.
|
||||
const LARGE_DIFF_LINES: usize = 5000;
|
||||
|
||||
/// Generates a matched old/new pair of roughly `lines` lines each, with a
|
||||
/// change every few lines so the diff has spread-out hunks.
|
||||
fn large_diff_pair(lines: usize) -> (String, String) {
|
||||
let mut old = String::with_capacity(lines * 48);
|
||||
let mut new = String::with_capacity(lines * 48);
|
||||
let mut index = 0usize;
|
||||
while old.lines().count() < lines {
|
||||
let i = index;
|
||||
let comment = format!("// Handler {i} in the generated dispatch table.\n");
|
||||
old.push_str(&comment);
|
||||
new.push_str(&comment);
|
||||
old.push_str(&format!("fn handle_{i}(payload: &str) -> bool {{\n"));
|
||||
new.push_str(&format!("fn handle_{i}(payload: &Payload) -> bool {{\n"));
|
||||
old.push_str(&format!(" payload.len() < {i} + 16\n"));
|
||||
new.push_str(&format!(
|
||||
" payload.size() < {i} + 16 && !payload.is_empty()\n"
|
||||
));
|
||||
if i.is_multiple_of(5) {
|
||||
old.push_str(&format!(" log::debug(\"handler {i} ran\");\n"));
|
||||
}
|
||||
if i.is_multiple_of(7) {
|
||||
new.push_str(" metrics::count(\"handle\");\n");
|
||||
}
|
||||
old.push_str("}\n\n");
|
||||
new.push_str("}\n\n");
|
||||
index += 1;
|
||||
}
|
||||
(old, new)
|
||||
}
|
||||
|
||||
impl DiffViewDemo {
|
||||
fn new(cx: &mut Context<Self>) -> Self {
|
||||
let diff =
|
||||
@@ -3494,7 +3500,7 @@ impl DiffViewDemo {
|
||||
// The panel is a cached view in the dock: it re-renders only when
|
||||
// the diff state it renders is notified.
|
||||
let subscription = cx.observe(&diff, |_, _, cx| cx.notify());
|
||||
let (large_old, large_new) = large_diff_pair(LARGE_DIFF_LINES);
|
||||
let (large_old, large_new) = Self::large_diff_pair(LARGE_DIFF_LINES);
|
||||
Self {
|
||||
diff,
|
||||
scenario: DiffScenario::Small,
|
||||
@@ -3503,6 +3509,36 @@ impl DiffViewDemo {
|
||||
_subscription: subscription,
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates a matched old/new pair of roughly `lines` lines each, with a
|
||||
/// change every few lines so the diff has spread-out hunks.
|
||||
fn large_diff_pair(lines: usize) -> (String, String) {
|
||||
let mut old = String::with_capacity(lines * 48);
|
||||
let mut new = String::with_capacity(lines * 48);
|
||||
let mut index = 0usize;
|
||||
while old.lines().count() < lines {
|
||||
let i = index;
|
||||
let comment = format!("// Handler {i} in the generated dispatch table.\n");
|
||||
old.push_str(&comment);
|
||||
new.push_str(&comment);
|
||||
old.push_str(&format!("fn handle_{i}(payload: &str) -> bool {{\n"));
|
||||
new.push_str(&format!("fn handle_{i}(payload: &Payload) -> bool {{\n"));
|
||||
old.push_str(&format!(" payload.len() < {i} + 16\n"));
|
||||
new.push_str(&format!(
|
||||
" payload.size() < {i} + 16 && !payload.is_empty()\n"
|
||||
));
|
||||
if i.is_multiple_of(5) {
|
||||
old.push_str(&format!(" log::debug(\"handler {i} ran\");\n"));
|
||||
}
|
||||
if i.is_multiple_of(7) {
|
||||
new.push_str(" metrics::count(\"handle\");\n");
|
||||
}
|
||||
old.push_str("}\n\n");
|
||||
new.push_str("}\n\n");
|
||||
index += 1;
|
||||
}
|
||||
(old, new)
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for DiffViewDemo {
|
||||
@@ -3548,7 +3584,7 @@ impl Render for DiffViewDemo {
|
||||
"unified, highlighted, virtualized",
|
||||
colors,
|
||||
div()
|
||||
.h(px(480.))
|
||||
.h(rems(30.))
|
||||
.rounded(radius.control)
|
||||
.border_1()
|
||||
.border_color(colors.border)
|
||||
@@ -3617,23 +3653,6 @@ That is the news.
|
||||
/// The section count of the large markdown sample.
|
||||
const LARGE_MARKDOWN_SECTIONS: usize = 1000;
|
||||
|
||||
/// Generates a long markdown document, so scrolling the view can be
|
||||
/// exercised on realistic content.
|
||||
fn large_markdown(sections: usize) -> String {
|
||||
let mut source = String::new();
|
||||
for section in 0..sections {
|
||||
source.push_str(&format!("## Section {section}\n\n"));
|
||||
source.push_str(&format!(
|
||||
"Handler {section} retries stale sockets and logs every outcome.\n\n"
|
||||
));
|
||||
source.push_str(&format!(
|
||||
"- [ ] Audit handler {section}\n- See [issue {section}](https://example.com/issues/{section})\n\n"
|
||||
));
|
||||
source.push_str("```rust\nfn handle() -> bool { true }\n```\n\n");
|
||||
}
|
||||
source
|
||||
}
|
||||
|
||||
struct MarkdownViewDemo {
|
||||
view: Entity<MarkdownViewState>,
|
||||
sample: MarkdownSample,
|
||||
@@ -3660,10 +3679,27 @@ impl MarkdownViewDemo {
|
||||
Self {
|
||||
view,
|
||||
sample: MarkdownSample::Guide,
|
||||
large_source: large_markdown(LARGE_MARKDOWN_SECTIONS).into(),
|
||||
large_source: Self::large_markdown(LARGE_MARKDOWN_SECTIONS).into(),
|
||||
_subscription: subscription,
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates a long markdown document, so scrolling the view can be
|
||||
/// exercised on realistic content.
|
||||
fn large_markdown(sections: usize) -> String {
|
||||
let mut source = String::new();
|
||||
for section in 0..sections {
|
||||
source.push_str(&format!("## Section {section}\n\n"));
|
||||
source.push_str(&format!(
|
||||
"Handler {section} retries stale sockets and logs every outcome.\n\n"
|
||||
));
|
||||
source.push_str(&format!(
|
||||
"- [ ] Audit handler {section}\n- See [issue {section}](https://example.com/issues/{section})\n\n"
|
||||
));
|
||||
source.push_str("```rust\nfn handle() -> bool { true }\n```\n\n");
|
||||
}
|
||||
source
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for MarkdownViewDemo {
|
||||
@@ -3704,7 +3740,7 @@ impl Render for MarkdownViewDemo {
|
||||
"rendered, virtualized, links clickable",
|
||||
colors,
|
||||
div()
|
||||
.h(px(480.))
|
||||
.h(rems(30.))
|
||||
.rounded(radius.control)
|
||||
.border_1()
|
||||
.border_color(colors.border)
|
||||
@@ -3741,8 +3777,8 @@ impl Render for SidebarDemo {
|
||||
"standalone; the app's own left sidebar is this component",
|
||||
colors,
|
||||
div()
|
||||
.w(px(280.))
|
||||
.h(px(360.))
|
||||
.w(rems(17.5))
|
||||
.h(rems(22.5))
|
||||
.rounded(radius.control)
|
||||
.border_1()
|
||||
.border_color(colors.border)
|
||||
@@ -3764,22 +3800,25 @@ impl Render for SidebarDemo {
|
||||
};
|
||||
vec![
|
||||
SidebarEntry::header("Favorites"),
|
||||
SidebarEntry::row("Documents").prefix(row_icon()),
|
||||
SidebarEntry::row("Pictures")
|
||||
SidebarEntry::row("Documents", "Documents")
|
||||
.prefix(row_icon()),
|
||||
SidebarEntry::row("Pictures", "Pictures")
|
||||
.prefix(row_icon())
|
||||
.detail("24")
|
||||
.suffix(
|
||||
Badge::new("sidebar-demo-new-badge", "new")
|
||||
.variant(BadgeVariant::Accent),
|
||||
),
|
||||
SidebarEntry::row("Archive").disabled(true),
|
||||
SidebarEntry::row("Archive", "Archive").disabled(true),
|
||||
SidebarEntry::header("Locations"),
|
||||
SidebarEntry::row("iCloud").prefix(row_icon()).suffix(
|
||||
StatusDot::new("sidebar-demo-icloud-status")
|
||||
.small()
|
||||
.variant(StatusDotVariant::Success),
|
||||
),
|
||||
SidebarEntry::row("Trash"),
|
||||
SidebarEntry::row("iCloud", "iCloud")
|
||||
.prefix(row_icon())
|
||||
.suffix(
|
||||
StatusDot::new("sidebar-demo-icloud-status")
|
||||
.small()
|
||||
.variant(StatusDotVariant::Success),
|
||||
),
|
||||
SidebarEntry::row("Trash", "Trash"),
|
||||
]
|
||||
}),
|
||||
),
|
||||
@@ -3798,6 +3837,12 @@ struct ResizeDemo {
|
||||
width: Pixels,
|
||||
}
|
||||
|
||||
impl ResizeDemo {
|
||||
const WIDTH_DEFAULT_REMS: f32 = 12.5;
|
||||
const WIDTH_MIN_REMS: f32 = 6.;
|
||||
const WIDTH_MAX_REMS: f32 = 20.;
|
||||
}
|
||||
|
||||
impl Render for ResizeDemo {
|
||||
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let view = cx.entity();
|
||||
@@ -3814,7 +3859,7 @@ impl Render for ResizeDemo {
|
||||
"seam",
|
||||
colors,
|
||||
h_flex()
|
||||
.h(px(112.))
|
||||
.h(rems(7.))
|
||||
.rounded(radius.control)
|
||||
.border_1()
|
||||
.border_color(colors.border)
|
||||
@@ -3831,15 +3876,21 @@ impl Render for ResizeDemo {
|
||||
)
|
||||
.child(
|
||||
ResizeHandle::new("resize-demo-handle")
|
||||
.on_resize(move |delta, _, cx| {
|
||||
.on_resize(move |delta, window, cx| {
|
||||
view.update(cx, |demo, cx| {
|
||||
demo.width = (demo.width + delta).clamp(px(96.), px(320.));
|
||||
demo.width = (demo.width + delta).clamp(
|
||||
rems(ResizeDemo::WIDTH_MIN_REMS)
|
||||
.to_pixels(window.rem_size()),
|
||||
rems(ResizeDemo::WIDTH_MAX_REMS)
|
||||
.to_pixels(window.rem_size()),
|
||||
);
|
||||
cx.notify();
|
||||
});
|
||||
})
|
||||
.on_reset(move |_, cx| {
|
||||
.on_reset(move |window, cx| {
|
||||
view_reset.update(cx, |demo, cx| {
|
||||
demo.width = px(200.);
|
||||
demo.width = rems(ResizeDemo::WIDTH_DEFAULT_REMS)
|
||||
.to_pixels(window.rem_size());
|
||||
cx.notify();
|
||||
});
|
||||
}),
|
||||
@@ -3871,7 +3922,7 @@ impl Render for ResizeDemo {
|
||||
struct ThemeDemo;
|
||||
|
||||
impl Render for ThemeDemo {
|
||||
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let theme = cx.theme();
|
||||
let colors = theme.colors;
|
||||
let radius = theme.radius;
|
||||
@@ -3928,7 +3979,7 @@ impl Render for ThemeDemo {
|
||||
(PresetChoice::TokyoNight, "Tokyo Night"),
|
||||
])
|
||||
.on_change(|next, _, cx| {
|
||||
set_preset(next.name(), cx).ok();
|
||||
set_preset(next.name(), cx).log_err();
|
||||
}),
|
||||
))
|
||||
.child(demo_section(
|
||||
@@ -3987,24 +4038,17 @@ impl Render for ThemeDemo {
|
||||
"icons (Lucide fallback; platform resolvers land later)",
|
||||
colors,
|
||||
div().flex().flex_wrap().items_center().gap_4().children(
|
||||
IconRole::all()
|
||||
.iter()
|
||||
.map(|role| icon(*role).size(px(20.)).color(colors.foreground)),
|
||||
IconRole::all().iter().map(|role| {
|
||||
icon(*role)
|
||||
.size(rems(1.25).to_pixels(window.rem_size()))
|
||||
.color(colors.foreground)
|
||||
}),
|
||||
),
|
||||
)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn button_label(variant: ButtonVariant) -> &'static str {
|
||||
match variant {
|
||||
ButtonVariant::Primary => "Primary",
|
||||
ButtonVariant::Secondary => "Secondary",
|
||||
ButtonVariant::Ghost => "Ghost",
|
||||
ButtonVariant::Destructive => "Delete",
|
||||
}
|
||||
}
|
||||
|
||||
fn section_label(text: &str, colors: gpui_native_theme::ColorTokens) -> impl IntoElement + use<> {
|
||||
div()
|
||||
.text_sm()
|
||||
@@ -4028,23 +4072,22 @@ where
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Without a client gpui installs a NullHttpClient and remote images —
|
||||
// the avatar pictures — silently never load.
|
||||
application()
|
||||
.with_assets(IconAssets)
|
||||
.with_http_client(std::sync::Arc::new(reqwest_client::ReqwestClient::new()))
|
||||
.run(|cx: &mut App| {
|
||||
gpui_native_core::init(cx);
|
||||
gpui_native_editor::code_view::bind_code_view_keys(cx);
|
||||
|
||||
cx.bind_keys([
|
||||
KeyBinding::new("cmd-k", OpenCommandPalette, None),
|
||||
KeyBinding::new("ctrl-k", OpenCommandPalette, None),
|
||||
]);
|
||||
|
||||
let layout_state = LayoutState::new(cx);
|
||||
let dock_state = DockState::new(cx);
|
||||
// A panel that declares a right sidebar view asks for it to open on
|
||||
// activation; visibility itself stays on the shell's LayoutState.
|
||||
let layout_for_dock = layout_state.clone();
|
||||
|
||||
dock_state.update(cx, |dock, _| {
|
||||
dock.on_open_right_sidebar(move |cx| {
|
||||
layout_for_dock.update(cx, |state, cx| {
|
||||
@@ -4052,29 +4095,27 @@ fn main() {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
let bounds = Bounds::centered(None, size(px(960.0), px(640.0)), cx);
|
||||
let theme = cx.theme();
|
||||
|
||||
if let Err(err) = cx.open_window(
|
||||
WindowOptions {
|
||||
window_bounds: Some(WindowBounds::Windowed(bounds)),
|
||||
titlebar: Some(gpui::TitlebarOptions {
|
||||
title: Some("gpui-native-kit gallery".into()),
|
||||
// The shell's strips are the titlebar; the native one only
|
||||
// hosts the traffic lights on macOS.
|
||||
appears_transparent: true,
|
||||
..Default::default()
|
||||
}),
|
||||
// The strips move the window themselves, so a tab dragged inside
|
||||
// the titlebar band never drags the window with it.
|
||||
|
||||
app_owns_titlebar_drag: true,
|
||||
// The platform's native backdrop; the theme's material paints
|
||||
// resolve over it and go opaque under reduced transparency.
|
||||
window_background: cx.theme().window_background,
|
||||
window_background: theme.window_background,
|
||||
..Default::default()
|
||||
},
|
||||
|window, cx| {
|
||||
Theme::sync_from_window(window, cx);
|
||||
Theme::sync_window_rem_size(window, cx);
|
||||
// Detached on purpose: the subscription lives as long as the window.
|
||||
|
||||
window
|
||||
.observe_window_appearance(|window, cx| {
|
||||
Theme::sync_from_window(window, cx);
|
||||
@@ -4082,13 +4123,11 @@ fn main() {
|
||||
cx.refresh_windows();
|
||||
})
|
||||
.detach();
|
||||
|
||||
let toasts = cx.new(|_| Toasts::default());
|
||||
let alerts = cx.new(|_| Alerts::default());
|
||||
|
||||
let gallery = cx.new(|cx| {
|
||||
// Toast pushes and dismissals repaint the stack.
|
||||
cx.observe(&toasts, |_, _, cx| cx.notify()).detach();
|
||||
// Alert pushes and dismissals repaint it.
|
||||
cx.observe(&alerts, |_, _, cx| cx.notify()).detach();
|
||||
Gallery {
|
||||
layout_state: layout_state.clone(),
|
||||
dock_state: dock_state.clone(),
|
||||
@@ -4099,8 +4138,15 @@ fn main() {
|
||||
dialog_open: false,
|
||||
alert_open: false,
|
||||
sidebar_transparent: true,
|
||||
_subscriptions: vec![
|
||||
// Toast pushes and dismissals repaint the stack.
|
||||
cx.observe(&toasts, |_, _, cx| cx.notify()),
|
||||
// Alert pushes and dismissals repaint it.
|
||||
cx.observe(&alerts, |_, _, cx| cx.notify()),
|
||||
],
|
||||
}
|
||||
});
|
||||
|
||||
let palette = cx.new(|cx| {
|
||||
let matcher = |query: &str, commands: &[Command]| {
|
||||
let needle = query.to_lowercase();
|
||||
@@ -4111,6 +4157,7 @@ fn main() {
|
||||
.collect()
|
||||
};
|
||||
let gallery_for_execute = gallery.downgrade();
|
||||
|
||||
CommandPaletteState::new(
|
||||
CommandPalette::new("gallery-palette", gallery_commands(), matcher)
|
||||
.on_execute(move |command: &Command, _, cx| {
|
||||
@@ -4119,16 +4166,18 @@ fn main() {
|
||||
gallery.palette_runs.push(command.id.clone());
|
||||
cx.notify();
|
||||
})
|
||||
.ok();
|
||||
.log_err();
|
||||
}),
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
|
||||
gallery.update(cx, |gallery, cx| {
|
||||
gallery.palette = Some(palette.clone());
|
||||
cx.notify();
|
||||
});
|
||||
|
||||
dock_state.update(cx, |dock, cx| {
|
||||
Gallery::register_panels(
|
||||
dock,
|
||||
@@ -4139,16 +4188,17 @@ fn main() {
|
||||
gallery.downgrade(),
|
||||
cx,
|
||||
);
|
||||
// TEMPORARY perf probe: open the field panel under the
|
||||
// autoplay env var so the probe types into a real window.
|
||||
|
||||
let first = if std::env::var("GPUI_FIELD_AUTOPLAY").is_ok() {
|
||||
eprintln!("[field-probe] autoplay: opening field panel");
|
||||
"field"
|
||||
} else {
|
||||
"welcome"
|
||||
};
|
||||
|
||||
dock.open_panel(first, window, cx);
|
||||
});
|
||||
|
||||
gallery
|
||||
},
|
||||
) {
|
||||
|
||||
Reference in New Issue
Block a user