diff --git a/crates/eww/src/config/eww_config.rs b/crates/eww/src/config/eww_config.rs index 71b9ea1..d1f3e42 100644 --- a/crates/eww/src/config/eww_config.rs +++ b/crates/eww/src/config/eww_config.rs @@ -1,9 +1,12 @@ use anyhow::{bail, Context, Result}; use eww_shared_util::VarName; use std::{collections::HashMap, path::Path}; -use yuck::config::{ - file_provider::YuckFiles, script_var_definition::ScriptVarDefinition, widget_definition::WidgetDefinition, - window_definition::WindowDefinition, Config, +use yuck::{ + config::{ + file_provider::YuckFiles, script_var_definition::ScriptVarDefinition, validate::ValidationError, + widget_definition::WidgetDefinition, window_definition::WindowDefinition, Config, + }, + error::AstError, }; use simplexpr::dynval::DynVal; @@ -52,6 +55,14 @@ impl EwwConfig { // run some validations on the configuration yuck::config::validate::validate(&config, super::inbuilt::get_inbuilt_vars().keys().cloned().collect())?; + for (name, def) in &config.widget_definitions { + if crate::widgets::widget_definitions::BUILTIN_WIDGET_NAMES.contains(&name.as_str()) { + return Err( + AstError::ValidationError(ValidationError::AccidentalBuiltinOverride(def.span, name.to_string())).into() + ); + } + } + let Config { widget_definitions, window_definitions, var_definitions, mut script_vars } = config; script_vars.extend(crate::config::inbuilt::get_inbuilt_vars()); diff --git a/crates/eww/src/widgets/widget_definitions.rs b/crates/eww/src/widgets/widget_definitions.rs index 97b8443..dc6930d 100644 --- a/crates/eww/src/widgets/widget_definitions.rs +++ b/crates/eww/src/widgets/widget_definitions.rs @@ -1,5 +1,5 @@ #![allow(clippy::option_map_unit_fn)] -use super::{build_widget::BuilderArgs, circular_progressbar::*, transform::*, run_command}; +use super::{build_widget::BuilderArgs, circular_progressbar::*, run_command, transform::*}; use crate::{ def_widget, enum_parse, error::DiagError, @@ -11,11 +11,11 @@ use anyhow::{anyhow, Context, Result}; use codespan_reporting::diagnostic::Severity; use eww_shared_util::Spanned; use gdk::{ModifierType, NotifyType}; +use glib::signal::SignalHandlerId; use gtk::{self, glib, prelude::*, DestDefaults, TargetEntry, TargetList}; use itertools::Itertools; use once_cell::sync::Lazy; use std::hash::Hasher; -use glib::signal::SignalHandlerId; use std::{ cell::RefCell, @@ -63,35 +63,57 @@ macro_rules! connect_signal_handler { }}; } - // TODO figure out how to // TODO https://developer.gnome.org/gtk3/stable/GtkFixed.html -//// widget definitions +pub const BUILTIN_WIDGET_NAMES: &[&str] = &[ + WIDGET_NAME_BOX, + WIDGET_NAME_CENTERBOX, + WIDGET_NAME_EVENTBOX, + WIDGET_NAME_CIRCULAR_PROGRESS, + WIDGET_NAME_GRAPH, + WIDGET_NAME_TRANSFORM, + WIDGET_NAME_SCALE, + WIDGET_NAME_PROGRESS, + WIDGET_NAME_IMAGE, + WIDGET_NAME_BUTTON, + WIDGET_NAME_LABEL, + WIDGET_NAME_LITERAL, + WIDGET_NAME_INPUT, + WIDGET_NAME_CALENDAR, + WIDGET_NAME_COLOR_BUTTON, + WIDGET_NAME_EXPANDER, + WIDGET_NAME_COLOR_CHOOSER, + WIDGET_NAME_COMBO_BOX_TEXT, + WIDGET_NAME_CHECKBOX, + WIDGET_NAME_REVEALER, + WIDGET_NAME_SCROLL, +]; +//// widget definitions pub(super) fn widget_use_to_gtk_widget(bargs: &mut BuilderArgs) -> Result { let gtk_widget = match bargs.widget_use.name.as_str() { - "box" => build_gtk_box(bargs)?.upcast(), - "centerbox" => build_center_box(bargs)?.upcast(), - "eventbox" => build_gtk_event_box(bargs)?.upcast(), - "circular-progress" => build_circular_progress_bar(bargs)?.upcast(), - "graph" => build_graph(bargs)?.upcast(), - "transform" => build_transform(bargs)?.upcast(), - "scale" => build_gtk_scale(bargs)?.upcast(), - "progress" => build_gtk_progress(bargs)?.upcast(), - "image" => build_gtk_image(bargs)?.upcast(), - "button" => build_gtk_button(bargs)?.upcast(), - "label" => build_gtk_label(bargs)?.upcast(), - "literal" => build_gtk_literal(bargs)?.upcast(), - "input" => build_gtk_input(bargs)?.upcast(), - "calendar" => build_gtk_calendar(bargs)?.upcast(), - "color-button" => build_gtk_color_button(bargs)?.upcast(), - "expander" => build_gtk_expander(bargs)?.upcast(), - "color-chooser" => build_gtk_color_chooser(bargs)?.upcast(), - "combo-box-text" => build_gtk_combo_box_text(bargs)?.upcast(), - "checkbox" => build_gtk_checkbox(bargs)?.upcast(), - "revealer" => build_gtk_revealer(bargs)?.upcast(), - "scroll" => build_gtk_scrolledwindow(bargs)?.upcast(), + WIDGET_NAME_BOX => build_gtk_box(bargs)?.upcast(), + WIDGET_NAME_CENTERBOX => build_center_box(bargs)?.upcast(), + WIDGET_NAME_EVENTBOX => build_gtk_event_box(bargs)?.upcast(), + WIDGET_NAME_CIRCULAR_PROGRESS => build_circular_progress_bar(bargs)?.upcast(), + WIDGET_NAME_GRAPH => build_graph(bargs)?.upcast(), + WIDGET_NAME_TRANSFORM => build_transform(bargs)?.upcast(), + WIDGET_NAME_SCALE => build_gtk_scale(bargs)?.upcast(), + WIDGET_NAME_PROGRESS => build_gtk_progress(bargs)?.upcast(), + WIDGET_NAME_IMAGE => build_gtk_image(bargs)?.upcast(), + WIDGET_NAME_BUTTON => build_gtk_button(bargs)?.upcast(), + WIDGET_NAME_LABEL => build_gtk_label(bargs)?.upcast(), + WIDGET_NAME_LITERAL => build_gtk_literal(bargs)?.upcast(), + WIDGET_NAME_INPUT => build_gtk_input(bargs)?.upcast(), + WIDGET_NAME_CALENDAR => build_gtk_calendar(bargs)?.upcast(), + WIDGET_NAME_COLOR_BUTTON => build_gtk_color_button(bargs)?.upcast(), + WIDGET_NAME_EXPANDER => build_gtk_expander(bargs)?.upcast(), + WIDGET_NAME_COLOR_CHOOSER => build_gtk_color_chooser(bargs)?.upcast(), + WIDGET_NAME_COMBO_BOX_TEXT => build_gtk_combo_box_text(bargs)?.upcast(), + WIDGET_NAME_CHECKBOX => build_gtk_checkbox(bargs)?.upcast(), + WIDGET_NAME_REVEALER => build_gtk_revealer(bargs)?.upcast(), + WIDGET_NAME_SCROLL => build_gtk_scrolledwindow(bargs)?.upcast(), _ => { return Err(AstError::ValidationError(ValidationError::UnknownWidget( bargs.widget_use.name_span, @@ -242,6 +264,7 @@ pub(super) fn resolve_orientable_attrs(bargs: &mut BuilderArgs, gtk_widget: >k // concrete widgets +const WIDGET_NAME_COMBO_BOX_TEXT: &'static str = "combo-box-text"; /// @widget combo-box-text /// @desc A combo box allowing the user to choose between several items. fn build_gtk_combo_box_text(bargs: &mut BuilderArgs) -> Result { @@ -264,6 +287,8 @@ fn build_gtk_combo_box_text(bargs: &mut BuilderArgs) -> Result Result { @@ -277,6 +302,7 @@ fn build_gtk_expander(bargs: &mut BuilderArgs) -> Result { Ok(gtk_widget) } +const WIDGET_NAME_REVEALER: &str = "revealer"; /// @widget revealer /// @desc A widget that can reveal a child with an animation. fn build_gtk_revealer(bargs: &mut BuilderArgs) -> Result { @@ -292,6 +318,7 @@ fn build_gtk_revealer(bargs: &mut BuilderArgs) -> Result { Ok(gtk_widget) } +const WIDGET_NAME_CHECKBOX: &str = "checkbox"; /// @widget a checkbox /// @desc A checkbox that can trigger events on checked / unchecked. fn build_gtk_checkbox(bargs: &mut BuilderArgs) -> Result { @@ -310,6 +337,7 @@ fn build_gtk_checkbox(bargs: &mut BuilderArgs) -> Result { Ok(gtk_widget) } +const WIDGET_NAME_COLOR_BUTTON: &str = "color-button"; /// @widget color-button /// @desc A button opening a color chooser window fn build_gtk_color_button(bargs: &mut BuilderArgs) -> Result { @@ -330,6 +358,7 @@ fn build_gtk_color_button(bargs: &mut BuilderArgs) -> Result { Ok(gtk_widget) } +const WIDGET_NAME_COLOR_CHOOSER: &str = "color-chooser"; /// @widget color-chooser /// @desc A color chooser widget fn build_gtk_color_chooser(bargs: &mut BuilderArgs) -> Result { @@ -350,6 +379,7 @@ fn build_gtk_color_chooser(bargs: &mut BuilderArgs) -> Result Result { @@ -373,6 +403,7 @@ fn build_gtk_scale(bargs: &mut BuilderArgs) -> Result { Ok(gtk_widget) } +const WIDGET_NAME_PROGRESS: &str = "progress"; /// @widget progress /// @desc A progress bar fn build_gtk_progress(bargs: &mut BuilderArgs) -> Result { @@ -390,6 +421,7 @@ fn build_gtk_progress(bargs: &mut BuilderArgs) -> Result { Ok(gtk_widget) } +const WIDGET_NAME_INPUT: &str = "input"; /// @widget input /// @desc An input field. For this to be useful, set `focusable="true"` on the window. fn build_gtk_input(bargs: &mut BuilderArgs) -> Result { @@ -418,6 +450,7 @@ fn build_gtk_input(bargs: &mut BuilderArgs) -> Result { Ok(gtk_widget) } +const WIDGET_NAME_BUTTON: &str = "button"; /// @widget button /// @desc A button fn build_gtk_button(bargs: &mut BuilderArgs) -> Result { @@ -450,6 +483,7 @@ fn build_gtk_button(bargs: &mut BuilderArgs) -> Result { Ok(gtk_widget) } +const WIDGET_NAME_IMAGE: &str = "image"; /// @widget image /// @desc A widget displaying an image fn build_gtk_image(bargs: &mut BuilderArgs) -> Result { @@ -471,6 +505,7 @@ fn build_gtk_image(bargs: &mut BuilderArgs) -> Result { Ok(gtk_widget) } +const WIDGET_NAME_BOX: &str = "box"; /// @widget box /// @desc the main layout container fn build_gtk_box(bargs: &mut BuilderArgs) -> Result { @@ -486,6 +521,7 @@ fn build_gtk_box(bargs: &mut BuilderArgs) -> Result { Ok(gtk_widget) } +const WIDGET_NAME_CENTERBOX: &str = "centerbox"; /// @widget centerbox /// @desc a box that must contain exactly three children, which will be layed out at the start, center and end of the container. fn build_center_box(bargs: &mut BuilderArgs) -> Result { @@ -534,6 +570,7 @@ fn build_center_box(bargs: &mut BuilderArgs) -> Result { } } +const WIDGET_NAME_SCROLL: &str = "scroll"; /// @widget scroll /// @desc a container with a single child that can scroll. fn build_gtk_scrolledwindow(bargs: &mut BuilderArgs) -> Result { @@ -554,6 +591,7 @@ fn build_gtk_scrolledwindow(bargs: &mut BuilderArgs) -> Result Result { @@ -710,6 +748,7 @@ fn build_gtk_event_box(bargs: &mut BuilderArgs) -> Result { Ok(gtk_widget) } +const WIDGET_NAME_LABEL: &str = "label"; /// @widget label /// @desc A text widget giving you more control over how the text is displayed fn build_gtk_label(bargs: &mut BuilderArgs) -> Result { @@ -745,6 +784,7 @@ fn build_gtk_label(bargs: &mut BuilderArgs) -> Result { Ok(gtk_widget) } +const WIDGET_NAME_LITERAL: &str = "literal"; /// @widget literal /// @desc A widget that allows you to render arbitrary yuck. fn build_gtk_literal(bargs: &mut BuilderArgs) -> Result { @@ -794,6 +834,7 @@ fn build_gtk_literal(bargs: &mut BuilderArgs) -> Result { Ok(gtk_widget) } +const WIDGET_NAME_CALENDAR: &str = "calendar"; /// @widget calendar /// @desc A widget that displays a calendar fn build_gtk_calendar(bargs: &mut BuilderArgs) -> Result { @@ -831,6 +872,7 @@ fn build_gtk_calendar(bargs: &mut BuilderArgs) -> Result { Ok(gtk_widget) } +const WIDGET_NAME_TRANSFORM: &str = "transform"; /// @widget transform /// @desc A widget that applies transformations to its content. They are applied in the following /// order: rotate->translate->scale) @@ -851,6 +893,7 @@ fn build_transform(bargs: &mut BuilderArgs) -> Result { Ok(w) } +const WIDGET_NAME_CIRCULAR_PROGRESS: &str = "circular-progress"; /// @widget circular-progress /// @desc A widget that displays a circular progress bar fn build_circular_progress_bar(bargs: &mut BuilderArgs) -> Result { @@ -868,6 +911,7 @@ fn build_circular_progress_bar(bargs: &mut BuilderArgs) -> Result { Ok(w) } +const WIDGET_NAME_GRAPH: &str = "graph"; /// @widget graph /// @desc A widget that displays a graph showing how a given value changes over time fn build_graph(bargs: &mut BuilderArgs) -> Result { diff --git a/crates/yuck/src/config/validate.rs b/crates/yuck/src/config/validate.rs index 054e66c..d165449 100644 --- a/crates/yuck/src/config/validate.rs +++ b/crates/yuck/src/config/validate.rs @@ -19,6 +19,9 @@ pub enum ValidationError { #[error("Unknown widget `{1}` referenced")] UnknownWidget(Span, String), + #[error("There is already a builtin widget called `{1}`")] + AccidentalBuiltinOverride(Span, String), + #[error("Missing attribute `{arg_name}` in use of widget `{widget_name}`")] MissingAttr { widget_name: String, arg_name: AttrName, arg_list_span: Option, use_span: Span }, @@ -37,6 +40,7 @@ impl Spanned for ValidationError { ValidationError::UnknownWidget(span, _) => *span, ValidationError::MissingAttr { use_span, .. } => *use_span, ValidationError::UnknownVariable { span, .. } => *span, + ValidationError::AccidentalBuiltinOverride(span, ..) => *span, } } } diff --git a/crates/yuck/src/format_diagnostic.rs b/crates/yuck/src/format_diagnostic.rs index 7da4ed7..ce7d627 100644 --- a/crates/yuck/src/format_diagnostic.rs +++ b/crates/yuck/src/format_diagnostic.rs @@ -194,6 +194,11 @@ impl ToDiagnostic for ValidationError { diag.with_notes(extra_notes) } + ValidationError::AccidentalBuiltinOverride(span, widget_name) => gen_diagnostic! { + msg = self, + label = span => "Defined here", + note = "Hint: Give your widget a different name. You could call it \"John\" for example. That's a cool name." + }, } } }