From 8446fe6de52fa68077d23d39f728f79a29b52f00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 18 Sep 2023 14:38:54 +0200 Subject: [PATCH] Implement theme selector in `editor` example --- core/src/text/highlighter.rs | 7 ++- examples/editor/src/main.rs | 101 ++++++++++++++++++++++++++++------- widget/src/text_editor.rs | 13 ++++- 3 files changed, 99 insertions(+), 22 deletions(-) diff --git a/core/src/text/highlighter.rs b/core/src/text/highlighter.rs index a929826f56..b462d08384 100644 --- a/core/src/text/highlighter.rs +++ b/core/src/text/highlighter.rs @@ -1,10 +1,9 @@ use crate::Color; -use std::hash::Hash; use std::ops::Range; pub trait Highlighter: 'static { - type Settings: Hash; + type Settings: PartialEq + Clone; type Highlight; type Iterator<'a>: Iterator, Self::Highlight)> @@ -13,6 +12,8 @@ pub trait Highlighter: 'static { fn new(settings: &Self::Settings) -> Self; + fn update(&mut self, new_settings: &Self::Settings); + fn change_line(&mut self, line: usize); fn highlight_line(&mut self, line: &str) -> Self::Iterator<'_>; @@ -38,6 +39,8 @@ impl Highlighter for PlainText { Self } + fn update(&mut self, _new_settings: &Self::Settings) {} + fn change_line(&mut self, _line: usize) {} fn highlight_line(&mut self, _line: &str) -> Self::Iterator<'_> { diff --git a/examples/editor/src/main.rs b/examples/editor/src/main.rs index 746496762c..fa35ba0ffc 100644 --- a/examples/editor/src/main.rs +++ b/examples/editor/src/main.rs @@ -1,5 +1,5 @@ -use iced::widget::{container, text_editor}; -use iced::{Element, Font, Sandbox, Settings, Theme}; +use iced::widget::{column, horizontal_space, pick_list, row, text_editor}; +use iced::{Element, Font, Length, Sandbox, Settings, Theme}; use highlighter::Highlighter; @@ -9,11 +9,13 @@ pub fn main() -> iced::Result { struct Editor { content: text_editor::Content, + theme: highlighter::Theme, } #[derive(Debug, Clone)] enum Message { Edit(text_editor::Action), + ThemeSelected(highlighter::Theme), } impl Sandbox for Editor { @@ -21,9 +23,8 @@ impl Sandbox for Editor { fn new() -> Self { Self { - content: text_editor::Content::with(include_str!( - "../../../README.md" - )), + content: text_editor::Content::with(include_str!("main.rs")), + theme: highlighter::Theme::SolarizedDark, } } @@ -36,18 +37,33 @@ impl Sandbox for Editor { Message::Edit(action) => { self.content.edit(action); } + Message::ThemeSelected(theme) => { + self.theme = theme; + } } } fn view(&self) -> Element { - container( + column![ + row![ + horizontal_space(Length::Fill), + pick_list( + highlighter::Theme::ALL, + Some(self.theme), + Message::ThemeSelected + ) + .padding([5, 10]) + ] + .spacing(10), text_editor(&self.content) .on_edit(Message::Edit) .font(Font::with_name("Hasklug Nerd Font Mono")) .highlight::(highlighter::Settings { - token: String::from("md"), + theme: self.theme, + extension: String::from("rs"), }), - ) + ] + .spacing(10) .padding(20) .into() } @@ -60,21 +76,52 @@ impl Sandbox for Editor { mod highlighter { use iced::advanced::text::highlighter; use iced::widget::text_editor; - use iced::{Color, Font, Theme}; + use iced::{Color, Font}; use std::ops::Range; use syntect::highlighting; use syntect::parsing::{self, SyntaxReference}; - #[derive(Debug, Clone, Hash)] + #[derive(Debug, Clone, PartialEq)] pub struct Settings { - pub token: String, + pub theme: Theme, + pub extension: String, + } + + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub enum Theme { + SolarizedDark, + InspiredGitHub, + Base16Mocha, + } + + impl Theme { + pub const ALL: &[Self] = + &[Self::SolarizedDark, Self::InspiredGitHub, Self::Base16Mocha]; + + fn key(&self) -> &'static str { + match self { + Theme::InspiredGitHub => "InspiredGitHub", + Theme::Base16Mocha => "base16-mocha.dark", + Theme::SolarizedDark => "Solarized (dark)", + } + } + } + + impl std::fmt::Display for Theme { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Theme::InspiredGitHub => write!(f, "Inspired GitHub"), + Theme::Base16Mocha => write!(f, "Mocha"), + Theme::SolarizedDark => write!(f, "Solarized Dark"), + } + } } pub struct Highlight(highlighting::StyleModifier); impl text_editor::Highlight for Highlight { - fn format(&self, _theme: &Theme) -> highlighter::Format { + fn format(&self, _theme: &iced::Theme) -> highlighter::Format { highlighter::Format { color: self.0.foreground.map(|color| { Color::from_rgba8( @@ -92,8 +139,8 @@ mod highlighter { pub struct Highlighter { syntaxes: parsing::SyntaxSet, syntax: SyntaxReference, - caches: Vec<(parsing::ParseState, parsing::ScopeStack)>, theme: highlighting::Theme, + caches: Vec<(parsing::ParseState, parsing::ScopeStack)>, current_line: usize, } @@ -110,26 +157,42 @@ mod highlighter { let syntaxes = parsing::SyntaxSet::load_defaults_nonewlines(); let syntax = syntaxes - .find_syntax_by_token(&settings.token) + .find_syntax_by_token(&settings.extension) .unwrap_or_else(|| syntaxes.find_syntax_plain_text()); - let parser = parsing::ParseState::new(syntax); - let stack = parsing::ScopeStack::new(); - let theme = highlighting::ThemeSet::load_defaults() .themes - .remove("base16-mocha.dark") + .remove(settings.theme.key()) .unwrap(); + let parser = parsing::ParseState::new(syntax); + let stack = parsing::ScopeStack::new(); + Highlighter { syntax: syntax.clone(), syntaxes, - caches: vec![(parser, stack)], theme, + caches: vec![(parser, stack)], current_line: 0, } } + fn update(&mut self, new_settings: &Self::Settings) { + self.syntax = self + .syntaxes + .find_syntax_by_token(&new_settings.extension) + .unwrap_or_else(|| self.syntaxes.find_syntax_plain_text()) + .clone(); + + self.theme = highlighting::ThemeSet::load_defaults() + .themes + .remove(new_settings.theme.key()) + .unwrap(); + + // Restart the highlighter + self.change_line(0); + } + fn change_line(&mut self, line: usize) { let snapshot = line / LINES_PER_SNAPSHOT; diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index c30e185f65..0cde2c983f 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -193,11 +193,12 @@ where } } -struct State { +struct State { is_focused: bool, last_click: Option, drag_click: Option, highlighter: RefCell, + highlighter_settings: Highlighter::Settings, } impl<'a, Highlighter, Message, Renderer> Widget @@ -220,6 +221,7 @@ where highlighter: RefCell::new(Highlighter::new( &self.highlighter_settings, )), + highlighter_settings: self.highlighter_settings.clone(), }) } @@ -240,6 +242,15 @@ where let mut internal = self.content.0.borrow_mut(); let state = tree.state.downcast_mut::>(); + if state.highlighter_settings != self.highlighter_settings { + state + .highlighter + .borrow_mut() + .update(&self.highlighter_settings); + + state.highlighter_settings = self.highlighter_settings.clone(); + } + internal.editor.update( limits.pad(self.padding).max(), self.font.unwrap_or_else(|| renderer.default_font()),