Skip to content

Commit 13f033b

Browse files
committed
Move label prefs to native Preferences menu; fix stray dots
1 parent 52b4aeb commit 13f033b

2 files changed

Lines changed: 156 additions & 74 deletions

File tree

‎src/main.rs‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ actions!(
1414
ToggleTheme,
1515
ToggleSidebar,
1616
ResetView,
17+
OpenPreferences,
1718
Quit
1819
]
1920
);
@@ -49,16 +50,22 @@ fn main() {
4950
cx.on_action(|_: &ToggleTheme, cx| with_view(cx, |v, cx| v.toggle_theme(cx)));
5051
cx.on_action(|_: &ToggleSidebar, cx| with_view(cx, |v, cx| v.toggle_sidebar(cx)));
5152
cx.on_action(|_: &ResetView, cx| with_view(cx, |v, cx| v.reset_view(cx)));
53+
cx.on_action(|_: &OpenPreferences, cx| with_view(cx, |v, cx| v.toggle_preferences(cx)));
5254
cx.on_action(|_: &Quit, cx| cx.quit());
5355
cx.bind_keys([
5456
KeyBinding::new("cmd-o", OpenFile, None),
5557
KeyBinding::new("cmd-r", ReloadGraph, None),
5658
KeyBinding::new("cmd-b", ToggleSidebar, None),
59+
KeyBinding::new("cmd-,", OpenPreferences, None),
5760
]);
5861
cx.set_menus(vec![
5962
Menu {
6063
name: "Bugscope".into(),
61-
items: vec![MenuItem::action("Quit", Quit)],
64+
items: vec![
65+
MenuItem::action("Preferences…", OpenPreferences),
66+
MenuItem::separator(),
67+
MenuItem::action("Quit", Quit),
68+
],
6269
},
6370
Menu {
6471
name: "File".into(),

‎src/ui.rs‎

Lines changed: 148 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,10 @@ pub struct RootView {
5555
selected: Option<usize>,
5656
schema_mode: bool,
5757
sidebar_open: bool,
58+
show_preferences: bool,
5859
/// `Some(true)` = force dark, `Some(false)` = light, `None` = follow OS.
5960
dark_override: Option<bool>,
60-
/// Label typeface + size for canvas labels (sidebar preferences).
61+
/// Label typeface + size for canvas labels (Preferences window).
6162
label_font: String,
6263
label_font_size: f32,
6364
/// Effective dark state from the last render — the theme toggle flips
@@ -86,7 +87,9 @@ impl RootView {
8687
/// the scanned directory, positional files are appended (first valid one
8788
/// is selected), plus `--limit` / `--schema` overrides.
8889
pub fn with_cli(opts: &CliOptions, cx: &mut Context<Self>) -> Self {
90+
// Canonicalize so the sidebar shows a real path, never a bare ".".
8991
let db_dir = opts.db_dir.clone().unwrap_or_else(backend::default_db_dir);
92+
let db_dir = db_dir.canonicalize().unwrap_or(db_dir);
9093
let mut databases = backend::scan_for_databases(&db_dir);
9194
for file in &opts.files {
9295
if !file.is_file() {
@@ -128,6 +131,7 @@ impl RootView {
128131
selected: None,
129132
schema_mode: opts.schema_mode,
130133
sidebar_open: false,
134+
show_preferences: false,
131135
dark_override: None,
132136
label_font: ".SystemUIFont".to_string(),
133137
label_font_size: 11.0,
@@ -270,6 +274,21 @@ impl RootView {
270274
cx.notify();
271275
}
272276

277+
pub fn toggle_preferences(&mut self, cx: &mut Context<Self>) {
278+
self.show_preferences = !self.show_preferences;
279+
cx.notify();
280+
}
281+
282+
/// Friendly display name: the default family starts with a literal
283+
/// dot, which reads as a stray glyph in UI text.
284+
pub fn label_font_display(&self) -> &str {
285+
self.label_font
286+
.trim_start_matches('.')
287+
.split(' ')
288+
.next()
289+
.unwrap_or(&self.label_font)
290+
}
291+
273292
pub fn set_label_font(&mut self, name: &str, cx: &mut Context<Self>) {
274293
self.label_font = name.to_string();
275294
cx.notify();
@@ -655,78 +674,6 @@ impl RootView {
655674
.child(d.name.clone()),
656675
);
657676
}
658-
col = col
659-
.child(div().pt_2().font_weight(FontWeight::BOLD).child("Labels"))
660-
.child(div().text_xs().child(format!(
661-
"{} · {:.0}pt",
662-
self.label_font, self.label_font_size
663-
)));
664-
{
665-
let mut row = div().flex().flex_row().flex_wrap().gap_1();
666-
for name in [".SystemUIFont", "Helvetica Neue", "Menlo", "Georgia"] {
667-
let active = self.label_font == name;
668-
let short = name
669-
.trim_start_matches('.')
670-
.split(' ')
671-
.next()
672-
.unwrap_or(name);
673-
let name_owned = name.to_string();
674-
row = row.child(
675-
div()
676-
.px_2()
677-
.py(px(2.))
678-
.rounded_md()
679-
.cursor_pointer()
680-
.text_xs()
681-
.bg(if active { theme.accent } else { theme.surface })
682-
.hover(|s| s.bg(theme.selection))
683-
.on_mouse_down(
684-
MouseButton::Left,
685-
cx.listener(move |view, _, _, cx| {
686-
view.set_label_font(&name_owned.clone(), cx);
687-
}),
688-
)
689-
.child(short.to_string()),
690-
);
691-
}
692-
col = col.child(row);
693-
}
694-
{
695-
let size_row = div()
696-
.flex()
697-
.flex_row()
698-
.items_center()
699-
.gap_1()
700-
.child(
701-
div()
702-
.px_2()
703-
.py(px(2.))
704-
.rounded_md()
705-
.cursor_pointer()
706-
.bg(theme.surface)
707-
.hover(|s| s.bg(theme.selection))
708-
.on_mouse_down(
709-
MouseButton::Left,
710-
cx.listener(|view, _, _, cx| view.bump_label_font_size(-1.0, cx)),
711-
)
712-
.child("A−"),
713-
)
714-
.child(
715-
div()
716-
.px_2()
717-
.py(px(2.))
718-
.rounded_md()
719-
.cursor_pointer()
720-
.bg(theme.surface)
721-
.hover(|s| s.bg(theme.selection))
722-
.on_mouse_down(
723-
MouseButton::Left,
724-
cx.listener(|view, _, _, cx| view.bump_label_font_size(1.0, cx)),
725-
)
726-
.child("A+"),
727-
);
728-
col = col.child(size_row);
729-
}
730677
col = col
731678
.child(div().pt_2().font_weight(FontWeight::BOLD).child("Matches"))
732679
.child(div().text_xs().child(format!(
@@ -965,6 +912,7 @@ impl Render for RootView {
965912
.flex()
966913
.flex_col()
967914
.size_full()
915+
.relative()
968916
.bg(theme.background)
969917
.text_color(theme.foreground)
970918
.child(self.render_header(theme, cx))
@@ -1089,6 +1037,133 @@ impl Render for RootView {
10891037
.child(query_content),
10901038
),
10911039
)
1040+
.children(self.render_preferences_modal(theme, cx))
1041+
}
1042+
}
1043+
1044+
impl RootView {
1045+
/// Native-style Preferences panel (app menu → Preferences…, ⌘,): a
1046+
/// centered modal with the label typeface + size controls. Rendered on
1047+
/// every platform from the same menu entry.
1048+
fn render_preferences_modal(&mut self, theme: Theme, cx: &mut Context<Self>) -> Option<Div> {
1049+
if !self.show_preferences {
1050+
return None;
1051+
}
1052+
let mut chips = div().flex().flex_row().flex_wrap().gap_1();
1053+
for name in [".SystemUIFont", "Helvetica Neue", "Menlo", "Georgia"] {
1054+
let active = self.label_font == name;
1055+
let short = name
1056+
.trim_start_matches('.')
1057+
.split(' ')
1058+
.next()
1059+
.unwrap_or(name);
1060+
let name_owned = name.to_string();
1061+
chips = chips.child(
1062+
div()
1063+
.px_2()
1064+
.py(px(2.))
1065+
.rounded_md()
1066+
.cursor_pointer()
1067+
.text_xs()
1068+
.bg(if active { theme.accent } else { theme.surface })
1069+
.hover(|s| s.bg(theme.selection))
1070+
.on_mouse_down(
1071+
MouseButton::Left,
1072+
cx.listener(move |view, _, _, cx| {
1073+
view.set_label_font(&name_owned.clone(), cx);
1074+
}),
1075+
)
1076+
.child(short.to_string()),
1077+
);
1078+
}
1079+
let panel = div()
1080+
.w(px(360.))
1081+
.rounded_lg()
1082+
.bg(theme.surface)
1083+
.border_1()
1084+
.border_color(theme.border)
1085+
.text_color(theme.foreground)
1086+
.p_4()
1087+
.flex()
1088+
.flex_col()
1089+
.gap_2()
1090+
.child(
1091+
div()
1092+
.text_lg()
1093+
.font_weight(FontWeight::BOLD)
1094+
.child("Preferences"),
1095+
)
1096+
.child(div().font_weight(FontWeight::BOLD).child("Label font"))
1097+
.child(div().text_xs().text_color(theme.secondary).child(format!(
1098+
"{} · {:.0}pt",
1099+
self.label_font_display(),
1100+
self.label_font_size
1101+
)))
1102+
.child(chips)
1103+
.child(
1104+
div()
1105+
.flex()
1106+
.flex_row()
1107+
.items_center()
1108+
.gap_1()
1109+
.child(
1110+
div()
1111+
.px_2()
1112+
.py(px(2.))
1113+
.rounded_md()
1114+
.cursor_pointer()
1115+
.bg(theme.inset)
1116+
.hover(|s| s.bg(theme.selection))
1117+
.on_mouse_down(
1118+
MouseButton::Left,
1119+
cx.listener(|view, _, _, cx| view.bump_label_font_size(-1.0, cx)),
1120+
)
1121+
.child("A−"),
1122+
)
1123+
.child(format!("{:.0}pt", self.label_font_size))
1124+
.child(
1125+
div()
1126+
.px_2()
1127+
.py(px(2.))
1128+
.rounded_md()
1129+
.cursor_pointer()
1130+
.bg(theme.inset)
1131+
.hover(|s| s.bg(theme.selection))
1132+
.on_mouse_down(
1133+
MouseButton::Left,
1134+
cx.listener(|view, _, _, cx| view.bump_label_font_size(1.0, cx)),
1135+
)
1136+
.child("A+"),
1137+
),
1138+
)
1139+
.child(
1140+
div().flex().flex_row().justify_end().child(
1141+
div()
1142+
.px_3()
1143+
.py_1()
1144+
.rounded_md()
1145+
.cursor_pointer()
1146+
.bg(theme.accent)
1147+
.hover(|s| s.bg(theme.selection))
1148+
.on_mouse_down(
1149+
MouseButton::Left,
1150+
cx.listener(|view, _, _, cx| view.toggle_preferences(cx)),
1151+
)
1152+
.child("Close"),
1153+
),
1154+
);
1155+
Some(
1156+
div()
1157+
.absolute()
1158+
.top(px(0.))
1159+
.left(px(0.))
1160+
.size_full()
1161+
.flex()
1162+
.items_center()
1163+
.justify_center()
1164+
.bg(black().opacity(0.45))
1165+
.child(panel),
1166+
)
10921167
}
10931168
}
10941169

0 commit comments

Comments
 (0)