use iced::advanced::text::highlighter::Format; use iced::border::Radius; use iced::widget::{button, hover, pane_grid, right, text, text_editor}; use iced::{Border, Font, Length}; use iced_custom_highlighter::{Highlight, Highlighter, Scope, Settings}; use iced_material::Theme; use super::style; use crate::icon; use crate::types::{DesignerPane, Message}; // TODO: implement a highlight style for the material theme //fn highlight_style(theme: &Theme, scope: &Scope) -> Format { // 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), // } //} fn highlight_style(theme: &Theme, scope: &Scope) -> Format { let colors = theme.colors(); let color = match scope { // Comments are low-priority and often de-emphasized Scope::Comment => colors.outline.variant, // Strings and escape sequences are content-rich Scope::String | Scope::QuotedString | Scope::QuotedSingle | Scope::EscapeSequence => colors.tertiary.color, // Regex deserves distinct treatment, often looking like code but acting like a string Scope::RegExp => colors.secondary.secondary_container, // Numeric literals and constants (user- or built-in) Scope::Number | Scope::BuiltinConstant | Scope::UserDefinedConstant => { colors.secondary.color } // Variable identifiers and function call references Scope::Variable | Scope::VariableFunction | Scope::VariableStart => { colors.primary.color } // Function and method declarations Scope::FunctionName | Scope::LibraryFunction => { colors.primary.primary_container } // Keywords and language operators Scope::Keyword | Scope::KeywordOperator | Scope::KeywordOther | Scope::Operator | Scope::Continuation => colors.secondary.secondary_container, // Module/class/namespace declarations Scope::Class | Scope::LibraryClass | Scope::StorageType | Scope::StorageModifier | Scope::Storage => colors.tertiary.tertiary_container, // Import paths or directives Scope::Import => colors.tertiary.color, // Error cases, invalid tokens Scope::Exception | Scope::Invalid => colors.error.color, // HTML-like tags or markup tokens Scope::TagName | Scope::TagStart => colors.primary.primary_container, // Special constants, macros, etc. Scope::Special | Scope::SupportConstruct => { colors.inverse.inverse_primary } // Braces, parentheses, brackets — structural but low priority Scope::Braces | Scope::Parantheses | Scope::Brackets => { colors.outline.variant } // Fallback Scope::Other => colors.surface.on_surface_variant, // Handle user-defined custom scope Scope::Custom { name, .. } => { if name.contains("comment") { colors.outline.variant } else if name.contains("error") { colors.error.color } else if name.contains("string") { colors.tertiary.color } else if name.contains("keyword") { colors.secondary.secondary_container } else if name.contains("function") { colors.primary.primary_container } else { // Default fallback for unknown custom scopes colors.surface.on_surface_variant } } }; Format { color: Some(color), font: None, } } pub fn view( editor_content: &text_editor::Content, is_focused: bool, ) -> pane_grid::Content<'_, Message, Theme> { let title_bar = pane_grid::TitleBar::new(text("Generated Code").center()) .controls(pane_grid::Controls::dynamic( button("Switch to Designer view") .on_press(DesignerPane::DesignerView.into()), button(icon::switch()).on_press(DesignerPane::DesignerView.into()), )) .padding(10) .style(style::title_bar); let editor = text_editor(editor_content) .on_action(Message::EditorAction) .font(Font::MONOSPACE) .highlight_with::>( Settings::new(vec![], highlight_style, "rs"), Highlight::to_format, ) .style(|theme, _| { let style = iced_material::text_editor::default( theme, text_editor::Status::Active, ); text_editor::Style { border: Border { radius: Radius::default(), ..style.border }, ..style } }) .height(Length::Fill) .padding(20); let copy = button(icon::copy().size(22).line_height(1.0).center()) .on_press(Message::CopyCode) .padding(3) .width(28) .style(|theme, status| { let style = iced_material::button::text(theme, status); button::Style { border: style.border.rounded(4), ..style } }); pane_grid::Content::new(hover(editor, right(copy).padding(16.0 * 0.875))) .title_bar(title_bar) .style(if is_focused { style::pane_focused } else { style::pane_active }) }