From 30ecb7d7160ea57a319479923d990b11db7bbac6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Thebault?= Date: Fri, 10 Jul 2026 19:04:57 +0200 Subject: [PATCH 1/3] Iced window gets a title or the title from the figure --- iced/src/show.rs | 24 ++++++++++++++++++++++-- src/drawing/figure.rs | 5 +++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/iced/src/show.rs b/iced/src/show.rs index 327f94af..25eaabc7 100644 --- a/iced/src/show.rs +++ b/iced/src/show.rs @@ -135,6 +135,7 @@ pub struct Params { pub style: Option, pub fontdb: Option>, pub commands: Commands, + pub title: Option, } impl Default for Params { @@ -143,6 +144,7 @@ impl Default for Params { style: None, fontdb: None, commands: Commands::all(), + title: None, } } } @@ -168,7 +170,7 @@ impl Show for des::Figure { .prepare(&*data_source, Some(&*fontdb)) .expect("Failed to prepare figure"); - show_app(fig, data_source, fontdb, params.style) + show_app(fig, data_source, fontdb, params.style, params.title) } } @@ -181,7 +183,7 @@ impl Show for drawing::PreparedFigure { .fontdb .unwrap_or_else(|| Arc::new(plotive::bundled_font_db())); - show_app(self, data_source, fontdb, params.style) + show_app(self, data_source, fontdb, params.style, params.title) } } @@ -190,6 +192,7 @@ fn show_app( data_source: Arc, fontdb: Arc, style: Option, + title: Option, ) -> iced::Result where D: data::Source + ?Sized + 'static, @@ -203,11 +206,13 @@ where let mut show = FigureShow::new(fontdb, Commands::all(), None); show.set_figure(fig, data_source.clone()); show.set_style(style.clone()); + show.set_title(title.clone()); (show, iced::Task::none()) }, FigureShow::update, FigureShow::view, ) + .title(FigureShow::title) // subscribe to key events .subscription(FigureShow::subscription) .run() @@ -274,6 +279,7 @@ pub struct FigureShow { fontdb: Arc, style: Option, commands: Commands, + title: Option, at_home: bool, over_plot: bool, tb_status: Option<(String, String)>, @@ -304,6 +310,7 @@ where at_home: true, over_plot: false, tb_status: None, + title: None, interaction: Interaction::None, middle_but_drag: None, fig_scale: 1.0, @@ -335,6 +342,19 @@ where self.style = style; } + pub fn set_title(&mut self, title: Option) { + self.title = title; + } + + pub fn title(&self) -> String { + if let Some(title) = self.fig.as_ref().and_then(|f| f.fig.title()) { + if let Some(fst_line) = title.lines().next() { + return fst_line.to_string(); + } + } + self.title.clone().unwrap_or_else(|| "Plotive Figure".to_string()) + } + pub fn figure(&self) -> Option<&drawing::PreparedFigure> { self.fig.as_ref().map(|f| &f.fig) } diff --git a/src/drawing/figure.rs b/src/drawing/figure.rs index 66adbc1a..fe176bc9 100644 --- a/src/drawing/figure.rs +++ b/src/drawing/figure.rs @@ -37,6 +37,11 @@ impl PreparedFigure { self.size } + /// Get the text of the figure title, if any. + pub fn title(&self) -> Option<&str> { + self.title.as_ref().map(|(_, text)| text.text.as_str()) + } + /// pub fn plot_indices(&self) -> impl Iterator + '_ { self.plots.iter_indices() From e9862d3b3673224c0e8ed1fe59f910fcc494583b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Thebault?= Date: Fri, 10 Jul 2026 19:23:10 +0200 Subject: [PATCH 2/3] iced theme follows plotive style --- iced/src/figure.rs | 1 + iced/src/show.rs | 125 ++++++++++++++++++++++++++++++++++++++------- 2 files changed, 108 insertions(+), 18 deletions(-) diff --git a/iced/src/figure.rs b/iced/src/figure.rs index 868c218b..b41f4439 100644 --- a/iced/src/figure.rs +++ b/iced/src/figure.rs @@ -372,6 +372,7 @@ pub fn map_style(theme: &iced::Theme) -> plotive::Style { iced::Theme::CatppuccinMacchiato => plotive::Style::catppuccin_macchiato(), iced::Theme::CatppuccinFrappe => plotive::Style::catppuccin_frappe(), iced::Theme::CatppuccinLatte => plotive::Style::catppuccin_latte(), + iced::Theme::Dracula => plotive::Style::dracula(), _ => { let theme = map_theme(theme); let palette = if theme.is_dark() { diff --git a/iced/src/show.rs b/iced/src/show.rs index 25eaabc7..0b1b8e54 100644 --- a/iced/src/show.rs +++ b/iced/src/show.rs @@ -187,6 +187,53 @@ impl Show for drawing::PreparedFigure { } } +fn color_to_iced(color: &plotive::Rgba8) -> iced::Color { + iced::Color::from_rgba( + color.r() as f32 / 255.0, + color.g() as f32 / 255.0, + color.b() as f32 / 255.0, + color.a() as f32 / 255.0, + ) +} + +fn theme_plotive_to_iced(pv_style: &plotive::Style) -> iced::Theme { + if pv_style == &plotive::Style::dracula() { + iced::Theme::Dracula + } else if pv_style == &plotive::Style::catppuccin_mocha() { + iced::Theme::CatppuccinMocha + } else if pv_style == &plotive::Style::catppuccin_latte() { + iced::Theme::CatppuccinLatte + } else if pv_style == &plotive::Style::catppuccin_frappe() { + iced::Theme::CatppuccinFrappe + } else if pv_style == &plotive::Style::catppuccin_macchiato() { + iced::Theme::CatppuccinMacchiato + } else { + let palette = iced::theme::Palette { + background: color_to_iced(&pv_style.theme().background()), + text: color_to_iced(&pv_style.theme().foreground()), + primary: color_to_iced( + &pv_style + .palette() + .get(plotive::style::series::IndexColor(0)), + ), + success: iced::theme::Palette::LIGHT.success, + warning: iced::theme::Palette::LIGHT.warning, + danger: iced::theme::Palette::LIGHT.danger, + }; + iced::Theme::custom("plotive", palette) + } +} + +fn theme_from_show(show: &FigureShow) -> iced::Theme +where + D: data::Source + ?Sized + 'static, +{ + show.style + .as_ref() + .map(theme_plotive_to_iced) + .unwrap_or(iced::Theme::Light) +} + fn show_app( fig: drawing::PreparedFigure, data_source: Arc, @@ -212,6 +259,7 @@ where FigureShow::update, FigureShow::view, ) + .theme(theme_from_show::) .title(FigureShow::title) // subscribe to key events .subscription(FigureShow::subscription) @@ -342,6 +390,13 @@ where self.style = style; } + pub fn theme(&self) -> iced::Theme { + self.style + .as_ref() + .map(theme_plotive_to_iced) + .unwrap_or(iced::Theme::Light) + } + pub fn set_title(&mut self, title: Option) { self.title = title; } @@ -352,7 +407,9 @@ where return fst_line.to_string(); } } - self.title.clone().unwrap_or_else(|| "Plotive Figure".to_string()) + self.title + .clone() + .unwrap_or_else(|| "Plotive Figure".to_string()) } pub fn figure(&self) -> Option<&drawing::PreparedFigure> { @@ -363,7 +420,7 @@ where self.fig.as_ref().map(|f| &f.data_source) } - pub fn style(&self) -> Option<&plotive::Style> { + pub fn plotive_style(&self) -> Option<&plotive::Style> { self.style.as_ref() } @@ -706,6 +763,19 @@ where /// Create a view for the toolbar pub fn toolbar_view(&self) -> iced::Element<'_, Message> { let has_fig = self.fig.is_some(); + let theme = self.theme(); + let icon_color = |style: fn(&iced::Theme, button::Status) -> button::Style, + enabled: bool| { + style( + &theme, + if enabled { + button::Status::Active + } else { + button::Status::Disabled + }, + ) + .text_color + }; let mut toolbar = row![]; @@ -724,17 +794,29 @@ where const FA_ZOOM: &str = "expand"; const FA_PAN: &str = "arrows-up-down-left-right"; - let home_button = button(fa_icon_solid(FA_HOME).size(ICON_SZ)) - .on_press_maybe((has_fig && !self.at_home).then_some(Message::GoHome)); - let zoom_button = button(fa_icon_solid(FA_ZOOM).size(ICON_SZ)) - .on_press_maybe(has_fig.then_some(Message::EnableZoom)); + let home_button = button( + fa_icon_solid(FA_HOME) + .size(ICON_SZ) + .color(icon_color(button::primary, has_fig && !self.at_home)), + ) + .on_press_maybe((has_fig && !self.at_home).then_some(Message::GoHome)); + let zoom_button = button( + fa_icon_solid(FA_ZOOM) + .size(ICON_SZ) + .color(icon_color(button::primary, has_fig)), + ) + .on_press_maybe(has_fig.then_some(Message::EnableZoom)); let zoom_button = if zooming { zoom_button.style(button::secondary) } else { zoom_button.style(button::primary) }; - let pan_button = button(fa_icon_solid(FA_PAN).size(ICON_SZ)) - .on_press_maybe(has_fig.then_some(Message::EnablePan)); + let pan_button = button( + fa_icon_solid(FA_PAN) + .size(ICON_SZ) + .color(icon_color(button::primary, has_fig)), + ) + .on_press_maybe(has_fig.then_some(Message::EnablePan)); let pan_button = if panning { pan_button.style(button::secondary) } else { @@ -758,9 +840,12 @@ where if self.commands.has_export_png() { let convert_png = button( - row![fa_icon_solid("file-image"), text("PNG").size(TEXT_SZ)] - .align_y(Alignment::Center) - .spacing(5), + row![ + fa_icon_solid("file-image").color(icon_color(button::primary, has_fig)), + text("PNG").size(TEXT_SZ) + ] + .align_y(Alignment::Center) + .spacing(5), ) .on_press_maybe(has_fig.then_some(Message::ExportPng)); toolbar = toolbar.push(convert_png); @@ -768,9 +853,12 @@ where if self.commands.has_export_svg() { let convert_svg = button( - row![fa_icon_solid("file-image"), text("SVG").size(TEXT_SZ)] - .align_y(Alignment::Center) - .spacing(5), + row![ + fa_icon_solid("file-image").color(icon_color(button::primary, has_fig)), + text("SVG").size(TEXT_SZ) + ] + .align_y(Alignment::Center) + .spacing(5), ) .on_press_maybe(has_fig.then_some(Message::ExportSvg)); toolbar = toolbar.push(convert_svg); @@ -779,9 +867,9 @@ where #[cfg(feature = "clipboard")] if self.commands.has_export_clipboard() { let icon = if self.ack_clipboard { - fa_icon_solid("check") + fa_icon_solid("check").color(icon_color(button::primary, has_fig)) } else { - fa_icon("clipboard") + fa_icon("clipboard").color(icon_color(button::primary, has_fig)) }; let convert_clipboard = @@ -791,8 +879,9 @@ where #[cfg(feature = "data-csv")] if self.commands.has_export_csv() { - let convert_csv = button(fa_icon_solid("file-csv")) - .on_press_maybe(has_fig.then_some(Message::ExportCsv)); + let convert_csv = + button(fa_icon_solid("file-csv").color(icon_color(button::primary, has_fig))) + .on_press_maybe(has_fig.then_some(Message::ExportCsv)); toolbar = toolbar.push(convert_csv); } From 4463b6f48dd8f54e9ffca1ec1d5611e94d5f8723 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Thebault?= Date: Sat, 11 Jul 2026 10:03:50 +0200 Subject: [PATCH 3/3] iced figure scale param --- iced/src/figure.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/iced/src/figure.rs b/iced/src/figure.rs index b41f4439..dba822e8 100644 --- a/iced/src/figure.rs +++ b/iced/src/figure.rs @@ -79,8 +79,8 @@ where } /// Set the scale of the [`Figure`] - pub fn scale(mut self, scale: impl Into) -> Self { - self.scale = scale.into(); + pub fn scale(mut self, scale: f32) -> Self { + self.scale = scale; self }