diff options
| -rw-r--r-- | assets/themes/rose_pine.toml | 41 | ||||
| -rw-r--r-- | src/appearance.rs | 46 | ||||
| -rw-r--r-- | src/config.rs | 41 | ||||
| -rw-r--r-- | src/dialogs.rs | 4 | ||||
| -rw-r--r-- | src/main.rs | 23 | ||||
| -rw-r--r-- | src/options.rs | 2 | ||||
| -rw-r--r-- | src/panes/code_view.rs | 46 | ||||
| -rw-r--r-- | src/panes/designer_view.rs | 7 | ||||
| -rw-r--r-- | src/panes/element_list.rs | 7 | ||||
| -rw-r--r-- | src/panes/style.rs | 23 | ||||
| -rw-r--r-- | src/theme.rs | 357 | ||||
| -rw-r--r-- | src/types.rs | 6 | ||||
| -rw-r--r-- | src/types/project.rs | 18 | ||||
| -rwxr-xr-x | src/types/rendered_element.rs | 30 | ||||
| -rw-r--r-- | src/widget.rs | 18 | ||||
| -rw-r--r-- | theme_test/src/main.rs | 2 |
16 files changed, 155 insertions, 516 deletions
diff --git a/assets/themes/rose_pine.toml b/assets/themes/rose_pine.toml deleted file mode 100644 index e4540fb..0000000 --- a/assets/themes/rose_pine.toml +++ /dev/null @@ -1,41 +0,0 @@ -name = "Rosé Pine" - -dark = true - -[palette] -background = "#26233a" -text = "#e0def4" -primary = "#9ccfd8" -success = "#f6c177" -danger = "#eb6f92" -warning = "#e4b363" - -[background] -base = { color = "#191724", text = "#e0def4" } -weak = { color = "#1f1d2e", text = "#e0def4" } -strong = { color = "#26233a", text = "#f4ebd3" } - -[primary] -base = { color = "#eb6f92", text = "#000000" } -weak = { color = "#f6c177", text = "#000000" } -strong = { color = "#ebbcba", text = "#000000" } - -[secondary] -base = { color = "#31748f", text = "#ffffff" } -weak = { color = "#9ccfd8", text = "#000000" } -strong = { color = "#c4a7e7", text = "#000000" } - -[success] -base = { color = "#9ccfd8", text = "#000000" } -weak = { color = "#6e6a86", text = "#ffffff" } -strong = { color = "#908caa", text = "#000000" } - -[danger] -base = { color = "#eb6f92", text = "#ffffff" } -weak = { color = "#f6c177", text = "#ffffff" } -strong = { color = "#524f67", text = "#ffffff" } - -[warning] -base = { color = "#e4b363", text = "#ffffff" } -weak = { color = "#f4e1a1", text = "#000000" } -strong = { color = "#d8a343", text = "#ffffff" } diff --git a/src/appearance.rs b/src/appearance.rs new file mode 100644 index 0000000..78e782d --- /dev/null +++ b/src/appearance.rs @@ -0,0 +1,46 @@ +use std::sync::Arc; + +use material_theme::Theme; + +pub fn iced_theme_from_str(theme_name: &str) -> iced::Theme { + match theme_name { + "Light" => iced::Theme::Light, + "Dark" => iced::Theme::Dark, + "Dracula" => iced::Theme::Dracula, + "Nord" => iced::Theme::Nord, + "Solarized Light" => iced::Theme::SolarizedLight, + "Solarized Dark" => iced::Theme::SolarizedDark, + "Gruvbox Light" => iced::Theme::GruvboxLight, + "Gruvbox Dark" => iced::Theme::GruvboxDark, + "Catppuccin Latte" => iced::Theme::CatppuccinLatte, + "Catppuccin Frappé" => iced::Theme::CatppuccinFrappe, + "Catppuccin Macchiato" => iced::Theme::CatppuccinMacchiato, + "Catppuccin Mocha" => iced::Theme::CatppuccinMocha, + "Tokyo Night" => iced::Theme::TokyoNight, + "Tokyo Night Storm" => iced::Theme::TokyoNightStorm, + "Tokyo Night Light" => iced::Theme::TokyoNightLight, + "Kanagawa Wave" => iced::Theme::KanagawaWave, + "Kanagawa Dragon" => iced::Theme::KanagawaDragon, + "Kanagawa Lotus" => iced::Theme::KanagawaLotus, + "Moonfly" => iced::Theme::Moonfly, + "Nightfly" => iced::Theme::Nightfly, + "Oxocarbon" => iced::Theme::Oxocarbon, + "Ferra" => iced::Theme::Ferra, + _ => iced::Theme::default(), + } +} + +#[derive(Debug, Clone)] +pub struct Appearance { + pub selected: Theme, + pub all: Arc<[Theme]>, +} + +impl Default for Appearance { + fn default() -> Self { + Self { + selected: Theme::default(), + all: Theme::ALL.into(), + } + } +} diff --git a/src/config.rs b/src/config.rs index 1da1239..7f6f8ce 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,26 +1,36 @@ // (c) 2022-2024 Cory Forsstrom, Casper Rogild Storm, Calvin Lee, Andrew Baldwin, Reza Alizadeh Majd // (c) 2024-2025 Polesznyák Márk László -use std::path::PathBuf; +use std::path::{Path, PathBuf}; +use std::sync::Arc; +use material_theme::Theme; use serde::Deserialize; use tokio_stream::StreamExt; use tokio_stream::wrappers::ReadDirStream; -use crate::theme::{Appearance, Theme, theme_from_str, theme_index}; +use crate::appearance::Appearance; use crate::{Error, environment}; #[derive(Debug, Clone, Default)] pub struct Config { - pub theme: Appearance, - pub last_project: Option<PathBuf>, + theme: Appearance, + last_project: Option<PathBuf>, } impl Config { - pub fn selected_theme(&self) -> iced::Theme { + pub fn selected_theme(&self) -> Theme { self.theme.selected.clone() } + pub fn themes(&self) -> Arc<[Theme]> { + self.theme.all.clone() + } + + pub fn last_project(&self) -> Option<&Path> { + self.last_project.as_deref() + } + pub fn config_dir() -> PathBuf { let dir = environment::config_dir(); @@ -67,7 +77,7 @@ impl Config { last_project, } = toml::from_str(content.as_ref())?; - let theme = Self::load_theme(theme).await.unwrap_or_default(); + let theme = Self::load_appearance(&theme).await.unwrap_or_default(); Ok(Self { theme, @@ -75,7 +85,9 @@ impl Config { }) } - pub async fn load_theme(theme_name: String) -> Result<Appearance, Error> { + pub async fn load_appearance( + theme_name: &str, + ) -> Result<Appearance, Error> { use tokio::fs; let read_entry = async move |entry: fs::DirEntry| { @@ -83,15 +95,16 @@ impl Config { let theme: Theme = toml::from_str(content.as_ref()).ok()?; - Some(iced::Theme::from(theme)) + Some(theme) }; - let mut selected = Theme::default().into(); - let mut all = iced::Theme::ALL.to_owned(); - all.push(Theme::default().into()); + let mut selected = Theme::default(); + let mut all = Theme::ALL.to_owned(); - if theme_index(&theme_name, iced::Theme::ALL).is_some() { - selected = theme_from_str(None, &theme_name); + if let Some(index) = + Theme::ALL.iter().position(|t| t.name() == theme_name) + { + selected = Theme::ALL[index].clone(); } let mut stream = @@ -102,7 +115,7 @@ impl Config { }; if let Some(theme) = read_entry(entry).await { - if theme.to_string() == theme_name { + if theme.name() == theme_name { selected = theme.clone(); } all.push(theme); diff --git a/src/dialogs.rs b/src/dialogs.rs index a623f35..c1933ec 100644 --- a/src/dialogs.rs +++ b/src/dialogs.rs @@ -1,8 +1,8 @@ -use iced::{Element, Task}; +use iced::Task; use iced_dialog::button; use crate::Message; -use crate::types::{DialogAction, DialogButtons}; +use crate::types::{DialogAction, DialogButtons, Element}; pub const UNSAVED_CHANGES_TITLE: &str = "Unsaved changes"; pub const WARNING_TITLE: &str = "Heads up!"; diff --git a/src/main.rs b/src/main.rs index 5014077..1ac1d67 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +mod appearance; mod config; mod dialogs; mod environment; @@ -6,7 +7,6 @@ mod error; mod icon; mod options; mod panes; -mod theme; mod types; mod values; mod widget; @@ -24,13 +24,15 @@ use iced::advanced::widget::Id; use iced::widget::{ Column, container, pane_grid, pick_list, row, text, text_editor, }; -use iced::{Alignment, Element, Length, Task, Theme, clipboard, keyboard}; +use iced::{Alignment, Length, Task, clipboard, keyboard}; use iced_anim::transition::Easing; use iced_anim::{Animated, Animation}; use iced_dialog::dialog::Dialog; +use material_theme::Theme; use panes::{code_view, designer_view, element_list}; use types::{ - Action, DesignerPane, DialogAction, DialogButtons, Message, Project, + Action, DesignerPane, DialogAction, DialogButtons, Element, Message, + Project, }; fn main() -> Result<(), Box<dyn std::error::Error>> { @@ -148,11 +150,10 @@ impl IcedBuilder { self.config = Arc::new(config); self.theme.update(self.config.selected_theme().into()); - return if let Some(path) = self.config.last_project.clone() - { + return if let Some(path) = self.config.last_project() { if path.exists() && path.is_file() { Task::perform( - Project::from_path(path), + Project::from_path(path.to_owned()), Message::FileOpened, ) } else { @@ -394,7 +395,7 @@ impl IcedBuilder { fn view(&self) -> Element<'_, Message> { let header = row![pick_list( - self.config.theme.all.clone(), + self.config.themes(), Some(self.theme.target()), |theme| Message::SwitchTheme(theme.into()) )] @@ -442,13 +443,7 @@ impl IcedBuilder { DialogButtons::OkCancel => vec![ok_button(), cancel_button()], }, ) - .title(self.dialog_title) - .container_style(|theme| container::Style { - background: Some( - theme.extended_palette().background.strong.color.into(), - ), - ..Default::default() - }); + .title(self.dialog_title); Animation::new(&self.theme, content) .on_update(Message::SwitchTheme) diff --git a/src/options.rs b/src/options.rs index 2dc25d7..931182a 100644 --- a/src/options.rs +++ b/src/options.rs @@ -258,7 +258,7 @@ impl<Message> ApplyOptions for Row<'_, Message> { } } -impl<Handle> ApplyOptions for Image<Handle> { +impl<Handle> ApplyOptions for Image<'_, Handle> { fn apply_options(self, options: BTreeMap<String, Option<String>>) -> Self { let mut image = self; diff --git a/src/panes/code_view.rs b/src/panes/code_view.rs index 890af8a..5999b8f 100644 --- a/src/panes/code_view.rs +++ b/src/panes/code_view.rs @@ -1,27 +1,36 @@ use iced::advanced::text::highlighter::Format; -use iced::widget::{Space, button, pane_grid, row, text, text_editor}; -use iced::{Alignment, Background, Border, Font, Length, Theme}; +use iced::border::Radius; +use iced::widget::{button, pane_grid, row, text, text_editor}; +use iced::{Alignment, Border, Font, Length}; use iced_custom_highlighter::{Highlight, Highlighter, Scope, Settings}; +use material_theme::Theme; use super::style; use crate::icon; use crate::types::{DesignerPane, Message}; use crate::widget::tip; +// TODO: implement a highlight style for the material theme fn highlight_style(theme: &Theme, scope: &Scope) -> Format<Font> { + let theme = if theme.is_dark() { + iced::Theme::SolarizedDark + } else { + iced::Theme::SolarizedLight + }; + match scope { Scope::Custom { .. } | Scope::Other => Format { color: Some(theme.extended_palette().primary.strong.color), font: None, }, - _ => Highlight::default_style(theme, scope), + _ => Highlight::default_style(&theme, scope), } } pub fn view( editor_content: &text_editor::Content, is_focused: bool, -) -> pane_grid::Content<'_, Message> { +) -> pane_grid::Content<'_, Message, Theme> { let title_bar = pane_grid::TitleBar::new(text("Generated Code").center()) .controls(pane_grid::Controls::dynamic( row![ @@ -29,28 +38,28 @@ pub fn view( button(icon::copy()) .on_press(Message::CopyCode) .padding([2, 7]) - .style(button::text), + .style(material_theme::button::text), "Copy", tip::Position::FollowCursor ), - Space::with_width(20), button("Switch to Designer view") .on_press(Message::SwitchPage(DesignerPane::DesignerView)) ] + .spacing(20) .align_y(Alignment::Center), row![ tip( button(icon::copy()) .on_press(Message::CopyCode) .padding([2, 7]) - .style(button::text), + .style(material_theme::button::text), "Copy", tip::Position::FollowCursor ), - Space::with_width(20), button(icon::switch()) .on_press(Message::SwitchPage(DesignerPane::DesignerView)) ] + .spacing(20) .align_y(Alignment::Center), )) .padding(10) @@ -60,25 +69,22 @@ pub fn view( text_editor(editor_content) .on_action(Message::EditorAction) .font(Font::MONOSPACE) - .highlight_with::<Highlighter>( + .highlight_with::<Highlighter<Theme>>( Settings::new(vec![], highlight_style, "rs"), Highlight::to_format, ) .style(|theme, _| { - let palette = theme.extended_palette(); + let style = material_theme::text_editor::default( + theme, + text_editor::Status::Active, + ); + text_editor::Style { - background: Background::Color( - palette.background.base.color, - ), border: Border { - radius: 2.into(), - width: 1.0, - color: palette.background.strong.color, + radius: Radius::default(), + ..style.border }, - icon: palette.background.weak.text, - placeholder: palette.background.strong.color, - value: palette.background.base.text, - selection: palette.primary.weak.color, + ..style } }) .height(Length::Fill) diff --git a/src/panes/designer_view.rs b/src/panes/designer_view.rs index af72022..0255b40 100644 --- a/src/panes/designer_view.rs +++ b/src/panes/designer_view.rs @@ -1,7 +1,8 @@ use iced::widget::{ button, center, container, pane_grid, responsive, row, text, themer, }; -use iced::{Alignment, Element, Length}; +use iced::{Alignment, Length}; +use material_theme::Theme; use super::style; use crate::icon; @@ -11,8 +12,8 @@ pub fn view<'a>( element_tree: Option<&'a RenderedElement>, designer_theme: iced::Theme, is_focused: bool, -) -> pane_grid::Content<'a, Message> { - let el_tree: Element<'a, Message> = match element_tree { +) -> pane_grid::Content<'a, Message, Theme> { + let el_tree: iced::Element<'a, Message> = match element_tree { Some(tree) => responsive(|size| { center( container(tree.clone()) diff --git a/src/panes/element_list.rs b/src/panes/element_list.rs index 594c203..0e5dbfe 100644 --- a/src/panes/element_list.rs +++ b/src/panes/element_list.rs @@ -1,9 +1,10 @@ use iced::widget::{Column, column, container, pane_grid, text}; -use iced::{Alignment, Element, Length}; +use iced::{Alignment, Length}; use iced_drop::droppable; +use material_theme::Theme; use super::style; -use crate::types::{ElementName, Message}; +use crate::types::{Element, ElementName, Message}; fn items_list_view<'a>() -> Element<'a, Message> { let mut column = Column::new() @@ -25,7 +26,7 @@ fn items_list_view<'a>() -> Element<'a, Message> { .into() } -pub fn view<'a>(is_focused: bool) -> pane_grid::Content<'a, Message> { +pub fn view<'a>(is_focused: bool) -> pane_grid::Content<'a, Message, Theme> { let items_list = items_list_view(); let content = column![items_list] .align_x(Alignment::Center) diff --git a/src/panes/style.rs b/src/panes/style.rs index 1eefb2d..acca6f9 100644 --- a/src/panes/style.rs +++ b/src/panes/style.rs @@ -1,24 +1,25 @@ use iced::widget::container::Style; -use iced::{Border, Theme}; +use iced::{Background, Border}; +use material_theme::Theme; pub fn title_bar(theme: &Theme) -> Style { - let palette = theme.extended_palette(); + let surface = theme.colors().surface; Style { - text_color: Some(palette.background.strong.text), - background: Some(palette.background.strong.color.into()), + text_color: Some(surface.on_surface), + background: Some(Background::Color(surface.surface_container.high)), ..Default::default() } } pub fn pane_active(theme: &Theme) -> Style { - let palette = theme.extended_palette(); + let surface = theme.colors().surface; Style { - background: Some(palette.background.weak.color.into()), + background: Some(Background::Color(surface.surface_container.low)), border: Border { width: 1.0, - color: palette.background.strong.color, + color: surface.surface_container.high, ..Border::default() }, ..Default::default() @@ -26,13 +27,13 @@ pub fn pane_active(theme: &Theme) -> Style { } pub fn pane_focused(theme: &Theme) -> Style { - let palette = theme.extended_palette(); + let surface = theme.colors().surface; Style { - background: Some(palette.background.weak.color.into()), + background: Some(Background::Color(surface.surface_container.low)), border: Border { - width: 4.0, - color: palette.background.strong.color, + width: 2.0, + color: surface.surface_container.high, ..Border::default() }, ..Default::default() diff --git a/src/theme.rs b/src/theme.rs deleted file mode 100644 index b721ddc..0000000 --- a/src/theme.rs +++ /dev/null @@ -1,357 +0,0 @@ -use std::sync::Arc; - -use iced::Color; -use iced::theme::palette::Extended; -use serde::Deserialize; - -use crate::config::Config; - -const DEFAULT_THEME_CONTENT: &str = - include_str!("../assets/themes/rose_pine.toml"); - -pub fn theme_index(theme_name: &str, slice: &[iced::Theme]) -> Option<usize> { - slice - .iter() - .position(|theme| theme.to_string() == theme_name) -} - -pub fn theme_from_str( - config: Option<&Config>, - theme_name: &str, -) -> iced::Theme { - match theme_name { - "Light" => iced::Theme::Light, - "Dark" => iced::Theme::Dark, - "Dracula" => iced::Theme::Dracula, - "Nord" => iced::Theme::Nord, - "Solarized Light" => iced::Theme::SolarizedLight, - "Solarized Dark" => iced::Theme::SolarizedDark, - "Gruvbox Light" => iced::Theme::GruvboxLight, - "Gruvbox Dark" => iced::Theme::GruvboxDark, - "Catppuccin Latte" => iced::Theme::CatppuccinLatte, - "Catppuccin Frappé" => iced::Theme::CatppuccinFrappe, - "Catppuccin Macchiato" => iced::Theme::CatppuccinMacchiato, - "Catppuccin Mocha" => iced::Theme::CatppuccinMocha, - "Tokyo Night" => iced::Theme::TokyoNight, - "Tokyo Night Storm" => iced::Theme::TokyoNightStorm, - "Tokyo Night Light" => iced::Theme::TokyoNightLight, - "Kanagawa Wave" => iced::Theme::KanagawaWave, - "Kanagawa Dragon" => iced::Theme::KanagawaDragon, - "Kanagawa Lotus" => iced::Theme::KanagawaLotus, - "Moonfly" => iced::Theme::Moonfly, - "Nightfly" => iced::Theme::Nightfly, - "Oxocarbon" => iced::Theme::Oxocarbon, - "Ferra" => iced::Theme::Ferra, - _ => { - if let Some(config) = config { - if theme_name == config.theme.selected.to_string() { - config.theme.selected.clone() - } else if let Some(index) = - theme_index(theme_name, &config.theme.all) - { - config.theme.all[index].clone() - } else { - iced::Theme::default() - } - } else { - iced::Theme::default() - } - } - } -} - -#[derive(Debug, Clone)] -pub struct Appearance { - pub selected: iced::Theme, - pub all: Arc<[iced::Theme]>, -} - -impl Default for Appearance { - fn default() -> Self { - Self { - selected: Theme::default().into(), - all: { - let mut themes = iced::Theme::ALL.to_owned(); - themes.push(Theme::default().into()); - themes.into() - }, - } - } -} - -#[derive(Debug, Deserialize)] -pub struct Theme { - name: String, - palette: ThemePalette, - dark: Option<bool>, - #[serde(flatten)] - extended: Option<ExtendedThemePalette>, -} - -impl From<Theme> for iced::Theme { - fn from(value: Theme) -> Self { - iced::Theme::custom_with_fn( - value.name.clone(), - value.palette.into(), - |_| value.into(), - ) - } -} - -impl Default for Theme { - fn default() -> Self { - toml::from_str(DEFAULT_THEME_CONTENT).expect("parse default theme") - } -} - -#[derive(Debug, Clone, Copy, Deserialize)] -pub struct ThemePalette { - #[serde(with = "color_serde")] - background: Color, - #[serde(with = "color_serde")] - text: Color, - #[serde(with = "color_serde")] - primary: Color, - #[serde(with = "color_serde")] - success: Color, - #[serde(with = "color_serde")] - danger: Color, - #[serde(with = "color_serde")] - warning: Color, -} - -impl Default for ThemePalette { - fn default() -> Self { - let palette = iced::Theme::default().palette(); - Self { - background: palette.background, - text: palette.text, - primary: palette.primary, - success: palette.success, - danger: palette.danger, - warning: palette.warning, - } - } -} - -impl From<ThemePalette> for iced::theme::Palette { - fn from(palette: ThemePalette) -> Self { - iced::theme::Palette { - background: palette.background, - text: palette.text, - primary: palette.primary, - success: palette.success, - danger: palette.danger, - warning: palette.warning, - } - } -} - -impl From<Theme> for Extended { - fn from(theme: Theme) -> Self { - let mut extended = Extended::generate(theme.palette.into()); - - if let Some(is_dark) = theme.dark { - extended.is_dark = is_dark; - } - - if let Some(extended_palette) = theme.extended { - if let Some(background) = extended_palette.background { - if let Some(base) = background.base { - extended.background.base = base.into(); - } - if let Some(weak) = background.weak { - extended.background.weak = weak.into(); - } - if let Some(strong) = background.strong { - extended.background.strong = strong.into(); - } - } - - if let Some(primary) = extended_palette.primary { - if let Some(base) = primary.base { - extended.primary.base = base.into(); - } - if let Some(weak) = primary.weak { - extended.primary.weak = weak.into(); - } - if let Some(strong) = primary.strong { - extended.primary.strong = strong.into(); - } - } - - if let Some(secondary) = extended_palette.secondary { - if let Some(base) = secondary.base { - extended.secondary.base = base.into(); - } - if let Some(weak) = secondary.weak { - extended.secondary.weak = weak.into(); - } - if let Some(strong) = secondary.strong { - extended.secondary.strong = strong.into(); - } - } - - if let Some(success) = extended_palette.success { - if let Some(base) = success.base { - extended.success.base = base.into(); - } - if let Some(weak) = success.weak { - extended.success.weak = weak.into(); - } - if let Some(strong) = success.strong { - extended.success.strong = strong.into(); - } - } - - if let Some(danger) = extended_palette.danger { - if let Some(base) = danger.base { - extended.danger.base = base.into(); - } - if let Some(weak) = danger.weak { - extended.danger.weak = weak.into(); - } - if let Some(strong) = danger.strong { - extended.danger.strong = strong.into(); - } - } - - if let Some(warning) = extended_palette.warning { - if let Some(base) = warning.base { - extended.warning.base = base.into(); - } - if let Some(weak) = warning.weak { - extended.warning.weak = weak.into(); - } - if let Some(strong) = warning.strong { - extended.warning.strong = strong.into(); - } - } - } - - extended - } -} - -#[derive(Debug, Clone, Copy, Default, Deserialize)] -struct ExtendedThemePalette { - background: Option<ThemeBackground>, - primary: Option<ThemePrimary>, - secondary: Option<ThemeSecondary>, - success: Option<ThemeSuccess>, - danger: Option<ThemeDanger>, - warning: Option<ThemeWarning>, -} - -#[derive(Debug, Clone, Copy, Default, Deserialize)] -struct ThemeBackground { - base: Option<ThemePair>, - weak: Option<ThemePair>, - strong: Option<ThemePair>, -} - -#[derive(Debug, Clone, Copy, Default, Deserialize)] -struct ThemePrimary { - base: Option<ThemePair>, - weak: Option<ThemePair>, - strong: Option<ThemePair>, -} - -#[derive(Debug, Clone, Copy, Default, Deserialize)] -struct ThemeSecondary { - base: Option<ThemePair>, - weak: Option<ThemePair>, - strong: Option<ThemePair>, -} - -#[derive(Debug, Clone, Copy, Default, Deserialize)] -struct ThemeSuccess { - base: Option<ThemePair>, - weak: Option<ThemePair>, - strong: Option<ThemePair>, -} - -#[derive(Debug, Clone, Copy, Default, Deserialize)] -struct ThemeDanger { - base: Option<ThemePair>, - weak: Option<ThemePair>, - strong: Option<ThemePair>, -} - -#[derive(Debug, Clone, Copy, Default, Deserialize)] -struct ThemeWarning { - base: Option<ThemePair>, - weak: Option<ThemePair>, - strong: Option<ThemePair>, -} - -#[derive(Debug, Clone, Copy, Default, Deserialize)] -struct ThemePair { - #[serde(with = "color_serde")] - color: Color, - #[serde(with = "color_serde")] - text: Color, -} - -impl From<ThemePair> for iced::theme::palette::Pair { - fn from(pair: ThemePair) -> Self { - Self { - color: pair.color, - text: pair.text, - } - } -} - -pub fn parse_argb(s: &str) -> Option<Color> { - let hex = s.strip_prefix('#').unwrap_or(s); - - let parse_channel = |from: usize, to: usize| { - let num = - usize::from_str_radix(&hex[from..=to], 16).ok()? as f32 / 255.0; - - // If we only got half a byte (one letter), expand it into a full byte (two letters) - Some(if from == to { num + num * 16.0 } else { num }) - }; - - Some(match hex.len() { - 3 => Color::from_rgb( - parse_channel(0, 0)?, - parse_channel(1, 1)?, - parse_channel(2, 2)?, - ), - 4 => Color::from_rgba( - parse_channel(1, 1)?, - parse_channel(2, 2)?, - parse_channel(3, 3)?, - parse_channel(0, 0)?, - ), - 6 => Color::from_rgb( - parse_channel(0, 1)?, - parse_channel(2, 3)?, - parse_channel(4, 5)?, - ), - 8 => Color::from_rgba( - parse_channel(2, 3)?, - parse_channel(4, 5)?, - parse_channel(6, 7)?, - parse_channel(0, 1)?, - ), - _ => None?, - }) -} - -mod color_serde { - use iced::Color; - use serde::{Deserialize, Deserializer}; - - use super::parse_argb; - - pub fn deserialize<'de, D>(deserializer: D) -> Result<Color, D::Error> - where - D: Deserializer<'de>, - { - Ok(String::deserialize(deserializer) - .map(|hex| parse_argb(&hex))? - .unwrap_or(Color::TRANSPARENT)) - } -} diff --git a/src/types.rs b/src/types.rs index adb788e..608f285 100644 --- a/src/types.rs +++ b/src/types.rs @@ -8,16 +8,20 @@ pub use element_name::ElementName; use iced::advanced::widget::Id; use iced::widget::{pane_grid, text_editor}; use iced_anim::Event; +use material_theme::Theme; pub use project::Project; pub use rendered_element::*; use crate::Error; use crate::config::Config; +pub type Element<'a, Message> = iced::Element<'a, Message, Theme>; + +#[allow(clippy::large_enum_variant)] #[derive(Debug, Clone)] pub enum Message { ConfigLoad(Result<Config, Error>), - SwitchTheme(Event<iced::Theme>), + SwitchTheme(Event<Theme>), CopyCode, SwitchPage(DesignerPane), EditorAction(text_editor::Action), diff --git a/src/types/project.rs b/src/types/project.rs index 91b2bb1..11789ac 100644 --- a/src/types/project.rs +++ b/src/types/project.rs @@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize}; use super::rendered_element::RenderedElement; use crate::Error; -use crate::theme::{theme_from_str, theme_index}; +use crate::appearance::iced_theme_from_str; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Project { @@ -33,8 +33,8 @@ impl Project { pub fn get_theme(&self) -> Theme { match &self.theme { - Some(theme) => theme_from_str(None, theme), - None => Theme::default(), + Some(theme) => iced_theme_from_str(theme), + None => iced::Theme::default(), } } @@ -90,20 +90,10 @@ impl Project { Some(ref element_tree) => { let (imports, view) = element_tree.codegen(); let theme = self.get_theme(); - let theme_code = theme.to_string().replace(" ", ""); - let mut theme_imports = ""; - if theme_index(&theme.to_string(), Theme::ALL).is_none() { - if theme_code.contains("Extended") { - theme_imports = "use iced::{{color,theme::{{Palette,palette::{{Extended,Background,Primary,Secondary,Success,Danger,Warning,Pair}}}}}};\n"; - } else { - theme_imports = "use iced::{{color,theme::Palette}};\n"; - } - } let app_code = format!( r#"// Automatically generated by iced Builder use iced::{{widget::{{{imports}}},Element}}; -{theme_imports} fn main() -> iced::Result {{ iced::application(State::default, State::update, State::view).title("{}").theme(State::theme).run() @@ -130,7 +120,7 @@ impl State {{ Some(ref t) => t, None => "New app", }, - theme_code + theme.to_string().replace(" ", "") ); let config = rust_format::Config::new_str() .edition(Edition::Rust2021) diff --git a/src/types/rendered_element.rs b/src/types/rendered_element.rs index 9639299..020aa46 100755 --- a/src/types/rendered_element.rs +++ b/src/types/rendered_element.rs @@ -1,7 +1,7 @@ use std::collections::BTreeMap; use iced::advanced::widget::Id; -use iced::{Element, Length, widget}; +use iced::{Element, widget}; use serde::{Deserialize, Serialize}; use crate::Error; @@ -171,34 +171,6 @@ impl RenderedElement { self } - pub fn text_view<'a>(self) -> Element<'a, Message> { - let mut children = widget::column![]; - - if let Some(els) = self.child_elements.clone() { - for el in els { - children = children.push(el.clone().text_view()); - } - } - iced_drop::droppable( - widget::container( - widget::column![ - widget::text(self.name.clone().to_string()), - children - ] - .width(Length::Fill) - .spacing(10), - ) - .padding(10) - .style(widget::container::bordered_box), - ) - .id(self.id().clone()) - .drag_hide(true) - .on_drop(move |point, rect| { - Message::MoveElement(self.clone(), point, rect) - }) - .into() - } - pub fn codegen(&self) -> (String, String) { let mut imports = String::new(); let mut view = String::new(); diff --git a/src/widget.rs b/src/widget.rs index f1eb0f3..859d25e 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -1,5 +1,7 @@ -use iced::Element; -use iced::widget::{container, text, tooltip}; +use iced::widget::{self, container, text, tooltip}; +use material_theme::Theme; + +use crate::types::Element; pub mod tip { pub use super::tooltip::Position; @@ -12,10 +14,16 @@ pub fn tip<'a, Message: 'a>( ) -> Element<'a, Message> { tooltip( target, - container(text(tip).size(14)) - .padding(5) - .style(container::rounded_box), + container(text(tip).size(14)).padding(5).style(|theme| { + let base = material_theme::container::surface_container_low(theme); + container::Style { + border: iced::border::rounded(4), + ..base + } + }), position, ) .into() } + +pub type Text<'a> = widget::Text<'a, Theme>; diff --git a/theme_test/src/main.rs b/theme_test/src/main.rs index b4a7731..cba4377 100644 --- a/theme_test/src/main.rs +++ b/theme_test/src/main.rs @@ -18,7 +18,7 @@ use material_theme::text::surface_variant; fn main() -> iced::Result { iced::application(State::default, State::update, State::view) - .theme(|state| *state.theme.value()) + .theme(|state| state.theme.value().clone()) .run() } |
