diff --git a/crates/moon-ui-gpui/src/settings/common.rs b/crates/moon-ui-gpui/src/settings/common.rs index ed144666..3d2958c8 100644 --- a/crates/moon-ui-gpui/src/settings/common.rs +++ b/crates/moon-ui-gpui/src/settings/common.rs @@ -3,7 +3,7 @@ //! //! Interface, Lines, and Connections reuse these helpers through re-exports in `settings/mod.rs`. -use std::collections::HashSet; +use std::{collections::HashSet, ops::RangeInclusive}; use gpui::*; use moon_ui::{ @@ -57,14 +57,34 @@ pub(super) fn hsla_u8(h: Hsla) -> [u8; 3] { ] } -/// Build an egui-style labeled slider row with the label above the slider and current value. +/// Build a labeled slider row with a scale below the track and a fixed-width current value. /// -/// The full-width label avoids clipping at large font sizes. Normalize IEEE `-0.0`, which slider -/// quantization over a negative subrange can otherwise display as `-0.00`. -pub(super) fn slider_row(label: &str, st: &Entity, cx: &App) -> impl IntoElement { +/// The full-width label avoids clipping at large font sizes. The caller supplies the slider range +/// because pinned MoonUI keeps it private, plus one formatter so both endpoints and the current +/// value use the field's unit and rounding contract. +/// +/// Args: +/// label: Localized caption displayed above the slider. +/// st: Slider state whose current value is displayed. +/// range: Inclusive endpoints displayed below the slider track. +/// format: Field-specific formatter shared by both endpoints and the current value. +/// cx: Application context used for palette and scaled geometry. +/// +/// Returns: +/// The assembled label, slider, scale, and current-value row. +pub(super) fn slider_row( + label: &str, + st: &Entity, + range: RangeInclusive, + format: impl Fn(f32) -> String, + cx: &App, +) -> impl IntoElement { let p = MoonPalette::active(cx); let val = st.read(cx).value().end(); let val = if val == 0.0 { 0.0 } else { val }; + let min = format(*range.start()); + let max = format(*range.end()); + let val = format(val); v_flex() .w_full() .child( @@ -79,15 +99,27 @@ pub(super) fn slider_row(label: &str, st: &Entity, cx: &App) -> .gap(design::ui_px(cx, 10.0)) .items_center() .child( - div() - .w(px(180.0)) - .child(MoonSlider::new(st).height(design::ui_value(cx, 22.0))), + v_flex() + .w(design::ui_px(cx, 360.0)) + .flex_none() + .child(MoonSlider::new(st).height(design::ui_value(cx, 22.0))) + .child( + h_flex() + .w_full() + .justify_between() + .text_size(design::t_caption(cx)) + .text_color(rgba_from(p.text_muted, 1.0)) + .child(min) + .child(max), + ), ) .child( div() - .w(design::font_w_px(cx, 58.0)) + .w(design::font_w_px(cx, 76.0)) + .flex_none() + .text_align(TextAlign::Right) .text_color(rgba_from(p.text_muted, 1.0)) - .child(format!("{val:.2}")), + .child(val), ), ) } diff --git a/crates/moon-ui-gpui/src/settings/general.rs b/crates/moon-ui-gpui/src/settings/general.rs index dbef311e..cede4930 100644 --- a/crates/moon-ui-gpui/src/settings/general.rs +++ b/crates/moon-ui-gpui/src/settings/general.rs @@ -7,7 +7,7 @@ use gpui::*; use moon_ui::{ MoonButton, MoonButtonSize, MoonCheckboxSize, MoonInput, MoonInputEvent, MoonInputState, MoonMenuSize, MoonPalette, MoonSelect, MoonSlider, MoonSliderEvent, MoonSliderState, - MoonToggle, StyledExt, h_flex, rgba_from, v_flex, + MoonToggle, MoonTooltipView, StyledExt, h_flex, rgba_from, v_flex, }; use rust_i18n::t; @@ -53,6 +53,43 @@ fn labeled_select( ) } +/// Render a muted settings hint, shortening multi-sentence text to its first sentence. +/// +/// A shortened hint retains its sentence-ending punctuation, adds an ellipsis, and exposes the +/// complete localized text in the standard wide settings tooltip. Text without a sentence +/// boundary is rendered unchanged and does not gain a tooltip. +/// +/// Args: +/// key: Stable localization key used as the tooltip host ID. +/// text: Complete localized hint text. +/// muted: Muted text colour from the active MoonUI palette. +/// +/// Returns: +/// The hint row, with a tooltip only when the visible text was shortened. +fn settings_hint(key: &'static str, text: &str, muted: Hsla) -> AnyElement { + let sentence_end = [". ", "! ", "? "] + .into_iter() + .filter_map(|boundary| text.find(boundary).map(|index| index + 1)) + .min(); + let Some(sentence_end) = sentence_end else { + return div() + .text_color(muted) + .child(text.to_string()) + .into_any_element(); + }; + + let full = text.to_string(); + div() + .id(key) + .text_color(muted) + .child(format!("{} …", &text[..sentence_end])) + .tooltip(move |_window, cx| { + cx.new(|_| MoonTooltipView::new(full.clone()).max_width(420.0)) + .into() + }) + .into_any_element() +} + impl SettingsView { /// Adjust the draft log-retention period, clamped to `0..=365` days. fn adjust_ret(&mut self, delta: i32, cx: &mut Context) { @@ -197,6 +234,12 @@ impl SettingsView { /// Build the General tab for UI mode/font, locale, chart grouping, control zones, /// Main-window idle closing, and file-log retention settings. + /// + /// Args: + /// cx: Settings context that supplies the active draft and palette. + /// + /// Returns: + /// The assembled General-tab content. pub(super) fn general_tab(&self, cx: &Context) -> impl IntoElement { let p = MoonPalette::active(cx); let muted = rgba_from(p.text_muted, 1.0); @@ -212,8 +255,6 @@ impl SettingsView { d.log_retention_days, ) }; - let hint = |s: &str| div().text_color(muted).child(s.to_string()); - // Remember the last valid enabled timeout and restore it when the checkbox is re-enabled. // The adjustment clamp keeps it at least 5; fall back to the default defensively. if idle_secs >= 5 { @@ -277,13 +318,25 @@ impl SettingsView { } })), ) - .child(hint(&t!("iface.light_theme_hint"))) + .child(settings_hint( + "iface.light_theme_hint", + &t!("iface.light_theme_hint"), + muted, + )) .child(self.font_delta_control(cx)) - .child(hint(&t!("iface.font_delta_hint"))) + .child(settings_hint( + "iface.font_delta_hint", + &t!("iface.font_delta_hint"), + muted, + )) .child(super::separator(p, cx)) // Interface locale selector. .child(labeled_select("general.language", &self.lang, 220.0, cx)) - .child(hint(&t!("general.language_hint"))) + .child(settings_hint( + "general.language_hint", + &t!("general.language_hint"), + muted, + )) .child(super::separator(p, cx)) // Which rate converts quote money to USDT. The hint explains the two limitations of // the current-rate conversion at the point where the application-wide choice is made. @@ -293,7 +346,11 @@ impl SettingsView { 260.0, cx, )) - .child(hint(&t!("general.valuation_mode_hint"))) + .child(settings_hint( + "general.valuation_mode_hint", + &t!("general.valuation_mode_hint"), + muted, + )) .child(super::separator(p, cx)) // Place each core in a separate chart tab. .child( @@ -308,7 +365,11 @@ impl SettingsView { .label(t!("general.charts_split_by_core").to_string()) .size(MoonCheckboxSize::Normal), ) - .child(hint(&t!("general.charts_split_by_core_hint"))) + .child(settings_hint( + "general.charts_split_by_core_hint", + &t!("general.charts_split_by_core_hint"), + muted, + )) .child(super::separator(p, cx)) // Restrict order and line controls to the order-book control zone. .child( @@ -323,7 +384,11 @@ impl SettingsView { .label(t!("general.separate_control_zones").to_string()) .size(MoonCheckboxSize::Normal), ) - .child(hint(&t!("general.separate_control_zones_hint"))) + .child(settings_hint( + "general.separate_control_zones_hint", + &t!("general.separate_control_zones_hint"), + muted, + )) .child(super::separator(p, cx)) // Close Main charts after window inactivity; zero disables the timeout. .child( @@ -369,7 +434,11 @@ impl SettingsView { Self::adjust_idle, )), ) - .child(hint(&t!("general.main_idle_close_hint"))) + .child(settings_hint( + "general.main_idle_close_hint", + &t!("general.main_idle_close_hint"), + muted, + )) .child(super::separator(p, cx)) // Stack layout is now configured per tab from the chart-tabs layout popup. // File logging and retention period. @@ -385,7 +454,11 @@ impl SettingsView { .label(t!("general.log_to_file").to_string()) .size(MoonCheckboxSize::Normal), ) - .child(hint(&t!("general.log_to_file_hint"))) + .child(settings_hint( + "general.log_to_file_hint", + &t!("general.log_to_file_hint"), + muted, + )) // Retention controls are enabled only while file logging is enabled; otherwise the // buttons are disabled and the value and labels are muted. .child( @@ -407,7 +480,11 @@ impl SettingsView { Self::adjust_ret, )), ) - .child(hint(&t!("general.log_retention_hint"))) + .child(settings_hint( + "general.log_retention_hint", + &t!("general.log_retention_hint"), + muted, + )) // Launch and servers.enc passwords. Last on the tab: it is the only block that can lock // the user out of their own cores, so it should not be the first thing a hand lands on. .child(super::separator(p, cx)) diff --git a/crates/moon-ui-gpui/src/settings/hotkeys/tab.rs b/crates/moon-ui-gpui/src/settings/hotkeys/tab.rs index 30e5eccb..c4fac38b 100644 --- a/crates/moon-ui-gpui/src/settings/hotkeys/tab.rs +++ b/crates/moon-ui-gpui/src/settings/hotkeys/tab.rs @@ -27,6 +27,12 @@ use super::{ use crate::design; use crate::settings::SettingsView; +/// Logical width reserved for every hotkey row title. +const ROW_TITLE_WIDTH: f32 = 160.0; + +/// Maximum readable width of a hotkey row description before its editor column begins. +const ROW_DESCRIPTION_MAX_WIDTH: f32 = 640.0; + impl SettingsView { pub(in crate::settings) fn hotkeys_tab(&self, cx: &Context) -> impl IntoElement { let hotkeys = { @@ -476,6 +482,17 @@ impl SettingsView { .into_any_element() } + /// Build one keyboard shortcut row with the editor in the tab's shared control column. + /// + /// Args: + /// title: Shortcut label shown in the fixed title column. + /// desc: Localized explanation that wraps within its description column. + /// slot: Hotkey configuration slot edited by the input. + /// hotkeys: Draft configuration used to show the current binding and conflicts. + /// cx: Settings context used for palette, scaling, and input events. + /// + /// Returns: + /// The rendered shortcut row. fn hotkey_row( &self, title: impl Into, @@ -505,26 +522,36 @@ impl SettingsView { .gap(design::ui_px(cx, 10.0)) .items_center() .child( - MoonText::new(title.into()) - .uppercase(false) - .mono(true) - .font_size(11.0) - .line_height(14.0) - .color(p.text) - .render(), + div() + .flex_none() + .w(design::ui_px(cx, ROW_TITLE_WIDTH)) + .child( + MoonText::new(title.into()) + .uppercase(false) + .mono(true) + .wrap() + .font_size(11.0) + .line_height(14.0) + .color(p.text) + .render(), + ), ) .child( // Match title sizing, use muted text, and wrap within the window. - div().flex_1().min_w_0().child( - MoonText::new(desc.into()) - .uppercase(false) - .mono(true) - .wrap() - .font_size(11.0) - .line_height(14.0) - .color(p.text_muted) - .render(), - ), + div() + .flex_1() + .min_w_0() + .max_w(design::ui_px(cx, ROW_DESCRIPTION_MAX_WIDTH)) + .child( + MoonText::new(desc.into()) + .uppercase(false) + .mono(true) + .wrap() + .font_size(11.0) + .line_height(14.0) + .color(p.text_muted) + .render(), + ), ) .child( MoonHotkeyInput::new(id) @@ -549,7 +576,8 @@ impl SettingsView { .into_any_element() } - /// One mouse-gesture row: the binding, and for a move row the "Move kind" beside it. + /// Build one mouse-gesture row with a binding and, for move rows, a "Move kind" selector. + /// The trailing controls wrap at narrow widths rather than clipping. /// /// Args: /// title: Row label. @@ -598,21 +626,22 @@ impl SettingsView { }) }); - let mut row = self.row_head(title.into(), desc.into(), disabled, cx); + let mut row = self + .row_head(title.into(), desc.into(), disabled, cx) + .child( + Self::row_dropdown(id, current.label()) + .trigger_variant(if current == MouseGestureBinding::None { + MoonButtonVariant::Neutral + } else { + MoonButtonVariant::Blue + }) + .menu_width_scaled(228.0) + .disabled(disabled) + .items(items), + ); if wip { row = row.child(self.wip_tag(&p, cx)); } - row = row.child( - Self::row_dropdown(id, current.label()) - .trigger_variant(if current == MouseGestureBinding::None { - MoonButtonVariant::Neutral - } else { - MoonButtonVariant::Blue - }) - .menu_width_scaled(228.0) - .disabled(disabled) - .items(items), - ); if let Some(kind_slot) = kind_slot { row = row.child(self.move_kind_dropdown(kind_slot, hotkeys, disabled)); } @@ -677,8 +706,18 @@ impl SettingsView { /// Builds the shared leading half of an editor row: title, then the wrapping description. /// - /// Every row on this tab is that pair plus one control, and the sizes are deliberately equal — - /// a description one step smaller was tried and read as a different font. + /// Every row on this tab is that pair plus one or two controls. The row wraps trailing controls + /// at narrow widths instead of clipping them, and the text sizes are deliberately equal — a + /// description one step smaller was tried and read as a different font. + /// + /// Args: + /// title: Label displayed in the shared fixed-width title column. + /// desc: Muted description that may wrap within its capped column. + /// disabled: Whether the title uses muted styling. + /// cx: Settings context used for palette and scaled layout. + /// + /// Returns: + /// The row prefix to which callers append one or two controls. fn row_head( &self, title: String, @@ -689,30 +728,41 @@ impl SettingsView { let p = MoonPalette::active(cx); h_flex() .w_full() + .flex_wrap() .min_h(design::fit_h_px(cx, 24.0, 12.0, 6.0)) .gap(design::ui_px(cx, 10.0)) .items_center() .child( - MoonText::new(title) - .uppercase(false) - .mono(true) - .font_size(11.0) - .line_height(14.0) - .color(if disabled { p.text_muted } else { p.text }) - .render(), + div() + .flex_none() + .w(design::ui_px(cx, ROW_TITLE_WIDTH)) + .child( + MoonText::new(title) + .uppercase(false) + .mono(true) + .wrap() + .font_size(11.0) + .line_height(14.0) + .color(if disabled { p.text_muted } else { p.text }) + .render(), + ), ) .child( // Match title sizing, use muted text, and wrap within the window. - div().flex_1().min_w_0().child( - MoonText::new(desc) - .uppercase(false) - .mono(true) - .wrap() - .font_size(11.0) - .line_height(14.0) - .color(p.text_muted) - .render(), - ), + div() + .flex_1() + .min_w_0() + .max_w(design::ui_px(cx, ROW_DESCRIPTION_MAX_WIDTH)) + .child( + MoonText::new(desc) + .uppercase(false) + .mono(true) + .wrap() + .font_size(11.0) + .line_height(14.0) + .color(p.text_muted) + .render(), + ), ) } @@ -763,13 +813,29 @@ impl SettingsView { .into_any_element() } + /// Builds the move-mirroring checkbox in the same control column as the gesture editors. + /// + /// Args: + /// hotkeys: Draft configuration that supplies the checkbox state. + /// cx: Settings context used for scaled layout and change events. + /// + /// Returns: + /// The aligned move-mirroring checkbox row. fn same_move_checkbox(&self, hotkeys: &HotkeysConfig, cx: &Context) -> AnyElement { let backend = self.backend.clone(); h_flex() .w_full() .min_h(design::fit_h_px(cx, 30.0, 12.0, 6.0)) + .gap(design::ui_px(cx, 10.0)) .items_center() + .child(div().flex_none().w(design::ui_px(cx, ROW_TITLE_WIDTH))) + .child( + div() + .flex_1() + .min_w_0() + .max_w(design::ui_px(cx, ROW_DESCRIPTION_MAX_WIDTH)), + ) .child( MoonCheckbox::new("same-hotkeys-for-move") .checked(hotkeys.same_hotkeys_for_move) diff --git a/crates/moon-ui-gpui/src/settings/interface.rs b/crates/moon-ui-gpui/src/settings/interface.rs index 554e3a89..dd569a1e 100644 --- a/crates/moon-ui-gpui/src/settings/interface.rs +++ b/crates/moon-ui-gpui/src/settings/interface.rs @@ -13,7 +13,10 @@ use rust_i18n::t; use super::{SettingsView, color_row, section, separator, slider_row}; use crate::Backend; -use moon_core::config::{ChartTheme, UiThemeMode}; +use moon_core::{ + config::{ChartTheme, UiThemeMode}, + util::fmt, +}; /// Theme editor state with one retained control entity per field. pub(super) struct Iface { @@ -239,6 +242,12 @@ impl SettingsView { /// /// Sections cover chart-label font, chart background/grid, crosshair, candles, order book, and /// panels. Personal light/dark mode and UI font settings belong to General in `settings.toml`. + /// + /// Args: + /// cx: Settings context that supplies the active palette and display scale. + /// + /// Returns: + /// The assembled Interface-tab content with formatted slider endpoints and values. pub(super) fn interface_tab(&self, cx: &Context) -> impl IntoElement { let i = &self.iface; let p = MoonPalette::active(cx); @@ -250,6 +259,13 @@ impl SettingsView { .child(slider_row( &t!("iface.label_font_delta"), &i.label_font_delta, + -4.0..=12.0, + |v| { + fmt::round_to(v as f64, 1).map_or_else( + || "+0.0 px".to_string(), + |rounded| format!("{rounded:+.1} px"), + ) + }, cx, )) .child(separator(p, cx)) @@ -257,15 +273,35 @@ impl SettingsView { .child(section(&t!("iface.sec_chart"), p, cx)) .child(color_row(&t!("iface.bg"), &i.bg, p, cx)) .child(color_row(&t!("iface.grid"), &i.grid, p, cx)) - .child(slider_row(&t!("iface.grid_alpha"), &i.grid_alpha, cx)) + .child(slider_row( + &t!("iface.grid_alpha"), + &i.grid_alpha, + 0.0..=1.0, + |v| { + fmt::pct((v * 100.0) as f64, 0) + .map_or_else(|| "0%".to_string(), |(text, _)| text) + }, + cx, + )) .child(separator(p, cx)) // Chart crosshair. .child(section(&t!("iface.sec_cross"), p, cx)) .child(color_row(&t!("iface.cross"), &i.cross, p, cx)) - .child(slider_row(&t!("iface.cross_alpha"), &i.cross_alpha, cx)) + .child(slider_row( + &t!("iface.cross_alpha"), + &i.cross_alpha, + 0.0..=1.0, + |v| { + fmt::pct((v * 100.0) as f64, 0) + .map_or_else(|| "0%".to_string(), |(text, _)| text) + }, + cx, + )) .child(slider_row( &t!("iface.cross_thickness"), &i.cross_thickness, + 0.5..=4.0, + |v| format!("{} px", fmt::compact(v as f64, 1)), cx, )) .child(separator(p, cx)) @@ -282,6 +318,11 @@ impl SettingsView { .child(slider_row( &t!("iface.candle_fill_alpha"), &i.candle_fill_alpha, + 0.0..=1.0, + |v| { + fmt::pct((v * 100.0) as f64, 0) + .map_or_else(|| "0%".to_string(), |(text, _)| text) + }, cx, )) .child(separator(p, cx)) @@ -291,15 +332,31 @@ impl SettingsView { .child(slider_row( &t!("iface.price_line_alpha"), &i.price_line_alpha, + 0.0..=1.0, + |v| { + fmt::pct((v * 100.0) as f64, 0) + .map_or_else(|| "0%".to_string(), |(text, _)| text) + }, cx, )) .child(color_row(&t!("iface.mark_line"), &i.mark_line, p, cx)) .child(slider_row( &t!("iface.mark_line_alpha"), &i.mark_line_alpha, + 0.0..=1.0, + |v| { + fmt::pct((v * 100.0) as f64, 0) + .map_or_else(|| "0%".to_string(), |(text, _)| text) + }, + cx, + )) + .child(slider_row( + &t!("iface.price_line_px"), + &i.price_line_px, + 0.5..=6.0, + |v| format!("{} px", fmt::compact(v as f64, 1)), cx, )) - .child(slider_row(&t!("iface.price_line_px"), &i.price_line_px, cx)) .child(separator(p, cx)) // The trade-mark and bottom-volume groups that used to sit here are now per chart TAB, // in the chart's palette popup (`chart_tabs::graphics_popup`). @@ -313,6 +370,11 @@ impl SettingsView { .child(slider_row( &t!("iface.book_level_alpha"), &i.book_level_alpha, + 0.0..=1.0, + |v| { + fmt::pct((v * 100.0) as f64, 0) + .map_or_else(|| "0%".to_string(), |(text, _)| text) + }, cx, )) .child(separator(p, cx)) diff --git a/crates/moon-ui-gpui/src/settings/lines.rs b/crates/moon-ui-gpui/src/settings/lines.rs index 642040e3..6ce4f23e 100644 --- a/crates/moon-ui-gpui/src/settings/lines.rs +++ b/crates/moon-ui-gpui/src/settings/lines.rs @@ -11,7 +11,10 @@ use moon_ui::{ use super::{SettingsView, separator, slider_row}; use crate::Backend; -use moon_core::config::{OrdersStyle, UiThemeMode}; +use moon_core::{ + config::{OrdersStyle, UiThemeMode}, + util::fmt, +}; use rust_i18n::t; /// Order-style checkbox descriptor: id, localized label, getter, and setter. @@ -285,6 +288,16 @@ impl SettingsView { /// Build an order-line section body with color, thickness, and dashed controls. /// When `markers` is enabled, also adds endpoint markers, cross dimensions, and knot controls. /// `checks` is either `[dashed]` or `[dashed, start, end, knots]`. + /// + /// Args: + /// cx: Settings context used for the active palette and scaled controls. + /// ed: Retained color and slider controls for the selected line type. + /// markers: Whether endpoint-marker and knot controls are available. + /// pending: Whether unfilled-order color and opacity controls are available. + /// checks: Checkbox descriptors in the line type's expected order. + /// + /// Returns: + /// The assembled, indented line-editor body. fn line_body( &self, cx: &Context, @@ -311,7 +324,13 @@ impl SettingsView { .gap(px(10.0)) .items_center() .child(MoonColorPicker::new(&ed.color)) - .child(slider_row(&t!("lines.thickness"), &ed.thickness, cx)), + .child(slider_row( + &t!("lines.thickness"), + &ed.thickness, + 0.5..=6.0, + |v| format!("{} px", fmt::compact(v as f64, 1)), + cx, + )), ) .child(chk(0)); // Only entry lines expose a separate color and opacity for unfilled orders (`fill == 0`). @@ -332,6 +351,11 @@ impl SettingsView { .child(slider_row( &t!("lines.pending_alpha"), &ed.pending_alpha, + 0.0..=1.0, + |v| { + fmt::pct((v * 100.0) as f64, 0) + .map_or_else(|| "0%".to_string(), |(text, _)| text) + }, cx, )), ); @@ -341,14 +365,28 @@ impl SettingsView { .child(separator(p, cx)) .child(chk(1)) .child(chk(2)) - .child(slider_row(&t!("lines.cross_size"), &ed.marker_size, cx)) + .child(slider_row( + &t!("lines.cross_size"), + &ed.marker_size, + 2.0..=24.0, + |v| format!("{} px", fmt::compact(v as f64, 1)), + cx, + )) .child(slider_row( &t!("lines.cross_thickness"), &ed.marker_thickness, + 0.5..=5.0, + |v| format!("{} px", fmt::compact(v as f64, 1)), cx, )) .child(chk(3)) - .child(slider_row(&t!("lines.knot_size"), &ed.knot_size, cx)); + .child(slider_row( + &t!("lines.knot_size"), + &ed.knot_size, + 1.0..=10.0, + |v| format!("{} px", fmt::compact(v as f64, 1)), + cx, + )); } col.into_any_element() } @@ -370,6 +408,12 @@ impl SettingsView { /// Build the Lines tab with one section per English trading line name, then Path and Global. /// Attribute labels come from `locales/lines.yml`. + /// + /// Args: + /// cx: Settings context used for the active palette and scaled controls. + /// + /// Returns: + /// The assembled Lines-tab content. pub(super) fn lines_tab(&self, cx: &Context) -> impl IntoElement { let l = &self.lines; v_flex() @@ -715,7 +759,13 @@ impl SettingsView { .gap(px(10.0)) .items_center() .child(MoonColorPicker::new(&l.path_color)) - .child(slider_row(&t!("lines.thickness"), &l.path_thickness, cx)), + .child(slider_row( + &t!("lines.thickness"), + &l.path_thickness, + 0.5..=6.0, + |v| format!("{} px", fmt::compact(v as f64, 1)), + cx, + )), ) .child(self.ord_check( cx, @@ -734,10 +784,24 @@ impl SettingsView { .font_bold() .child(t!("lines.global").to_string()), ) - .child(slider_row(&t!("lines.active_alpha"), &l.active_alpha, cx)) + .child(slider_row( + &t!("lines.active_alpha"), + &l.active_alpha, + 0.05..=1.0, + |v| { + fmt::pct((v * 100.0) as f64, 0) + .map_or_else(|| "0%".to_string(), |(text, _)| text) + }, + cx, + )) .child(slider_row( &t!("lines.closed_visibility"), &l.closed_alpha, + 0.0..=1.0, + |v| { + fmt::pct((v * 100.0) as f64, 0) + .map_or_else(|| "0%".to_string(), |(text, _)| text) + }, cx, )) .child(self.ord_check( @@ -747,7 +811,13 @@ impl SettingsView { |o| o.pending_dashed, |o, v| o.pending_dashed = v, )) - .child(slider_row(&t!("lines.max_closed"), &l.max_closed, cx)) + .child(slider_row( + &t!("lines.max_closed"), + &l.max_closed, + 0.0..=5000.0, + |v| fmt::compact(v as f64, 0), + cx, + )) .child( div() .mt_2() diff --git a/crates/moon-ui-gpui/tests/theme_contract/analytics.rs b/crates/moon-ui-gpui/tests/theme_contract/analytics.rs index 7ebf7279..35549811 100644 --- a/crates/moon-ui-gpui/tests/theme_contract/analytics.rs +++ b/crates/moon-ui-gpui/tests/theme_contract/analytics.rs @@ -2239,7 +2239,7 @@ fn the_valuation_mode_selector_lives_in_settings_and_wakes_every_surface() { for needle in [ "\"general.valuation_mode\"", "&self.valuation", - "hint(&t!(\"general.valuation_mode_hint\"))", + "settings_hint(\n \"general.valuation_mode_hint\",\n &t!(\"general.valuation_mode_hint\"),", ] { assert!( tab.contains(needle), diff --git a/locales/general.yml b/locales/general.yml index 0bfe7cef..1a6277d0 100644 --- a/locales/general.yml +++ b/locales/general.yml @@ -47,9 +47,9 @@ general.main_idle_close: en: "Auto-close Main charts when idle" es: "Cierre automático de gráficos Main al estar inactivo" general.main_idle_close_secs: - ru: "Закрывать через, сек" - en: "Close after, sec" - es: "Cerrar tras, seg" + ru: "Закрывать через" + en: "Close after" + es: "Cerrar tras" general.main_idle_close_hint: ru: "Неактивность = окно Main не в фокусе ЛИБО в фокусе, но мышь не двигается. Каждый график закрывается через N сек своей неактивности (недавно открытый — последним); фулскрин тоже закроется и режим выключится. Подписки на стакан снимаются сразу." en: "Idle = the Main window is not focused OR focused but the mouse is not moving. Each chart closes after N seconds of its own inactivity (the most recently opened closes last); a fullscreen chart closes too and exits fullscreen. Order-book subscriptions are dropped immediately." @@ -87,9 +87,9 @@ general.log_to_file_hint: en: "App and core logs are written to logs/_.log (one file per source per day)." es: "Los registros de app y núcleos se escriben en logs/_.log (un archivo por fuente al día)." general.log_retention: - ru: "Хранить лог, дней" - en: "Keep log, days" - es: "Conservar registro, días" + ru: "Хранить лог" + en: "Keep log" + es: "Conservar registro" general.log_retention_hint: ru: "Файлы старше указанного срока удаляются при запуске и раз в сутки. 0 — хранить всё." en: "Files older than this are deleted on startup and once a day. 0 — keep everything." diff --git a/locales/interface.yml b/locales/interface.yml index 4ab483e4..78cb9ee3 100644 --- a/locales/interface.yml +++ b/locales/interface.yml @@ -31,9 +31,9 @@ iface.font_delta_hint: en: "Font scale of the whole MoonUI interface. Applies instantly." es: "Escala de fuente de toda la interfaz MoonUI. Se aplica al instante." iface.label_font_delta: - ru: "Размер подписей линий и курсора (чарт)" - en: "Order-line & cursor label size (chart)" - es: "Tamaño de etiquetas de líneas y cursor (gráfico)" + ru: "Коррекция размера подписей линий и курсора (px)" + en: "Order-line & cursor label size adjustment (px)" + es: "Ajuste de tamaño de etiquetas de líneas y cursor (px)" iface.sec_chart: ru: "График: фон и сетка" en: "Chart: background & grid" diff --git a/locales/lines.yml b/locales/lines.yml index 3b979957..01c7ca57 100644 --- a/locales/lines.yml +++ b/locales/lines.yml @@ -58,21 +58,21 @@ lines.global: en: "Global" es: "Global" lines.active_alpha: - ru: "прозрачность активных" - en: "active alpha" - es: "opacidad activa" + ru: "Прозрачность активных" + en: "Active alpha" + es: "Opacidad activa" lines.closed_visibility: - ru: "видимость отменённых/закрытых" - en: "cancelled/closed visibility" - es: "visibilidad cancel./cerradas" + ru: "Видимость отменённых/закрытых" + en: "Cancelled/closed visibility" + es: "Visibilidad cancel./cerradas" lines.pending_dashed: - ru: "отлож. ордер: пунктир входа" - en: "pending order: dashed entry" - es: "orden pendiente: entrada discontinua" + ru: "Отлож. ордер: пунктир входа" + en: "Pending order: dashed entry" + es: "Orden pendiente: entrada discontinua" lines.max_closed: - ru: "макс. закрытых ордеров на графике" - en: "max closed orders drawn" - es: "máx. órdenes cerradas dibujadas" + ru: "Макс. закрытых ордеров на графике" + en: "Max closed orders drawn" + es: "Máx. órdenes cerradas dibujadas" lines.hint: ru: "Линии Stop/Trailing/Liq появляются только после исполнения входа." en: "Stop/Trailing/Liq lines appear only after the entry is filled."