feat(config): Allow custom widgets to make use of children (#317)
This commit is contained in:
parent
0aaaa2c2b8
commit
48bfb1e0c0
3 changed files with 68 additions and 5 deletions
|
@ -112,10 +112,7 @@ pub fn generate_generic_widget_node(
|
||||||
w: WidgetUse,
|
w: WidgetUse,
|
||||||
) -> AstResult<Box<dyn WidgetNode>> {
|
) -> AstResult<Box<dyn WidgetNode>> {
|
||||||
if let Some(def) = defs.get(&w.name) {
|
if let Some(def) = defs.get(&w.name) {
|
||||||
if !w.children.is_empty() {
|
let children_span = w.children_span();
|
||||||
return Err(AstError::TooManyNodes(w.children_span(), 0).note("User-defined widgets cannot be given children."));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut new_local_env = w
|
let mut new_local_env = w
|
||||||
.attrs
|
.attrs
|
||||||
.attrs
|
.attrs
|
||||||
|
@ -129,7 +126,9 @@ pub fn generate_generic_widget_node(
|
||||||
new_local_env.entry(var_name).or_insert_with(|| SimplExpr::literal(expected.span, String::new()));
|
new_local_env.entry(var_name).or_insert_with(|| SimplExpr::literal(expected.span, String::new()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let content = generate_generic_widget_node(defs, &new_local_env, def.widget.clone())?;
|
let definition_content = replace_children_placeholder_in(children_span, def.widget.clone(), &w.children)?;
|
||||||
|
|
||||||
|
let content = generate_generic_widget_node(defs, &new_local_env, definition_content)?;
|
||||||
Ok(Box::new(UserDefined { name: w.name, span: w.span, content }))
|
Ok(Box::new(UserDefined { name: w.name, span: w.span, content }))
|
||||||
} else {
|
} else {
|
||||||
Ok(Box::new(Generic {
|
Ok(Box::new(Generic {
|
||||||
|
@ -151,3 +150,33 @@ pub fn generate_generic_widget_node(
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Replaces all the `children` placeholders in the given [`widget`](w) using the provided [`children`](provided_children).
|
||||||
|
fn replace_children_placeholder_in(use_span: Span, mut w: WidgetUse, provided_children: &[WidgetUse]) -> AstResult<WidgetUse> {
|
||||||
|
// Take the current children from the widget and replace them with an empty vector that we will now add widgets to again.
|
||||||
|
let child_count = w.children.len();
|
||||||
|
let widget_children = std::mem::replace(&mut w.children, Vec::with_capacity(child_count));
|
||||||
|
|
||||||
|
for mut child in widget_children.into_iter() {
|
||||||
|
if child.name == "children" {
|
||||||
|
// Note that we use `primitive_optional` here, meaning that the value for `nth` must be static.
|
||||||
|
// We'll be able to make this dynamic after the state management structure rework
|
||||||
|
if let Some(nth) = child.attrs.primitive_optional::<usize, _>("nth")? {
|
||||||
|
// If a single child is referenced, push that single widget into the children
|
||||||
|
let selected_child: &WidgetUse = provided_children
|
||||||
|
.get(nth)
|
||||||
|
.ok_or_else(|| AstError::MissingNode(use_span).context_label(child.span, "required here"))?;
|
||||||
|
w.children.push(selected_child.clone());
|
||||||
|
} else {
|
||||||
|
// otherwise append all provided children
|
||||||
|
w.children.append(&mut provided_children.to_vec());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If this isn't a `children`-node, then recursively go into it and replace the children there.
|
||||||
|
// If there are no children referenced in there, this will append the widget unchanged.
|
||||||
|
let child = replace_children_placeholder_in(use_span, child, provided_children)?;
|
||||||
|
w.children.push(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(w)
|
||||||
|
}
|
||||||
|
|
|
@ -82,6 +82,8 @@ impl Attributes {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieve a required attribute from the set which _must not_ reference any variables,
|
||||||
|
/// and is thus known to be static.
|
||||||
pub fn primitive_required<T, E>(&mut self, key: &str) -> Result<T, AstError>
|
pub fn primitive_required<T, E>(&mut self, key: &str) -> Result<T, AstError>
|
||||||
where
|
where
|
||||||
E: std::error::Error + 'static + Sync + Send,
|
E: std::error::Error + 'static + Sync + Send,
|
||||||
|
@ -95,6 +97,8 @@ impl Attributes {
|
||||||
.map_err(|e| AttrError::Other(ast.span(), Box::new(e)))?)
|
.map_err(|e| AttrError::Other(ast.span(), Box::new(e)))?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieve an optional attribute from the set which _must not_ reference any variables,
|
||||||
|
/// and is thus known to be static.
|
||||||
pub fn primitive_optional<T, E>(&mut self, key: &str) -> Result<Option<T>, AstError>
|
pub fn primitive_optional<T, E>(&mut self, key: &str) -> Result<Option<T>, AstError>
|
||||||
where
|
where
|
||||||
E: std::error::Error + 'static + Sync + Send,
|
E: std::error::Error + 'static + Sync + Send,
|
||||||
|
|
|
@ -127,6 +127,36 @@ To then use our widget, we call it just like we would use any other built-in wid
|
||||||
As you may have noticed, we are using a couple predefined widgets here. These are all listed and explained in the [widgets chapter](widgets.md).
|
As you may have noticed, we are using a couple predefined widgets here. These are all listed and explained in the [widgets chapter](widgets.md).
|
||||||
|
|
||||||
|
|
||||||
|
### Rendering children in your widgets
|
||||||
|
(Note that this feature is currently considered **unstable**. The API might change, and there might be bugs here. If you encounter any, please do report them.)
|
||||||
|
|
||||||
|
As your configuration grows, you might want to improve the structure of you config by factoring out functionality into basic reusable widgets.
|
||||||
|
Eww allows you to create custom wrapper widgets that can themselves take children, just like some of the built-in widgets like `box` or `button` can.
|
||||||
|
For this, use the `children` placeholder:
|
||||||
|
```lisp
|
||||||
|
(defwidget labeled-container [name]
|
||||||
|
(box :class "container"
|
||||||
|
name
|
||||||
|
(children)))
|
||||||
|
```
|
||||||
|
Now you can use this widget as expected:
|
||||||
|
```lisp
|
||||||
|
(labeled-container :name "foo"
|
||||||
|
(button :onclick "notify-send hey ho"
|
||||||
|
"click me"))
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also create more complex structure by referring to specific children with the `nth`-attribute:
|
||||||
|
```lisp
|
||||||
|
(defwidget two-boxes []
|
||||||
|
(box
|
||||||
|
(box :class "first" (children :nth 0))
|
||||||
|
(box :class "second" (children :nth 1))))
|
||||||
|
```
|
||||||
|
**NOTE**: It is currently not possible to dynamically change which child is shown.
|
||||||
|
This means that `nth` needs to be set to a static value, and cannot refer to a variable.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Adding dynamic content
|
## Adding dynamic content
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue