diff --git a/docs/Configuration guide.md b/docs/Configuration guide.md
index c269106c..7caeaf55 100644
--- a/docs/Configuration guide.md
+++ b/docs/Configuration guide.md
@@ -332,6 +332,7 @@ For information on the `Script` type, and embedding scripts in strings, see [her
| `show_if` | [Dynamic Boolean](dynamic-values#dynamic-boolean) | `null` | Polls the script to check its exit code. If exit code is zero, the module is shown. For other codes, it is hidden. |
| `transition_type` | `slide_start` or `slide_end` or `crossfade` or `none` | `slide_start` | The transition animation to use when showing/hiding the widget. |
| `transition_duration` | `integer` | `250` | The length of the transition animation to use when showing/hiding the widget. |
+| `disable_popup` | `boolean` | `false` | Prevents the popup from opening on-click for this widget. |
#### Appearance
diff --git a/docs/modules/Custom.md b/docs/modules/Custom.md
index 298e0a3b..30dbc488 100644
--- a/docs/modules/Custom.md
+++ b/docs/modules/Custom.md
@@ -1,6 +1,11 @@
-Allows you to compose custom modules consisting of multiple widgets, including popups.
+Allows you to compose custom modules consisting of multiple modules and widgets, including popups.
Labels can display dynamic content from scripts, and buttons can interact with the bar or execute commands on click.
+The module provides a set of utility widgets, such as containers, labels and buttons.
+In addition to these, you can also add any native module.
+Paired with the other custom modules such as Cairo,
+this provides a powerful declarative interface for constructing your own interfaces.
+
If you only intend to run a single script, prefer the [script](script) module,
or [label](label) if you only need a single text label.
@@ -13,6 +18,11 @@ or [label](label) if you only need a single text label.
This module can be quite fiddly to configure as you effectively have to build a tree of widgets by hand.
It is well worth looking at the examples.
+| Name | Type | Default | Description |
+|---------|------------------------|------------|------------------------------------------|
+| `bar` | `(Module or Widget)[]` | `[]` | Modules and widgets to add to the bar. |
+| `popup` | `(Module or Widget)[]` | `null` | Modules and widgets to add to the popup. |
+
### `Widget`
There are many widget types, each with their own config options.
@@ -36,7 +46,7 @@ A container to place nested widgets inside.
| Name | Type | Default | Description |
|---------------|------------------------------------------------------------|----------------|-------------------------------------------------------------------|
| `orientation` | `'horizontal'` or `'vertical'` (shorthand: `'h'` or `'v'`) | `'horizontal'` | Whether child widgets should be horizontally or vertically added. |
-| `widgets` | `Widget[]` | `[]` | List of widgets to add to this box. |
+| `widgets` | `(Module or Widget)[]` | `[]` | List of widgets to add to this box. |
#### Label
@@ -197,6 +207,7 @@ to help get your head around what's going on:
+
@@ -252,6 +263,10 @@ to help get your head around what's going on:
"label": "Uptime: {{30000:uptime -p | cut -d ' ' -f2-}}",
"name": "uptime",
"type": "label"
+ },
+ {
+ "type": "clock",
+ "disable_popup": true
}
]
}
@@ -309,6 +324,10 @@ type = 'button'
label = '''Uptime: {{30000:uptime -p | cut -d ' ' -f2-}}'''
name = 'uptime'
type = 'label'
+
+[[end.popup.widgets]]
+type = 'clock'
+disable_popup = true
```
@@ -345,6 +364,8 @@ end:
- label: 'Uptime: {{30000:uptime -p | cut -d '' '' -f2-}}'
name: uptime
type: label
+ - type: clock
+ disable_popup: true
type: custom
```
@@ -370,6 +391,7 @@ let {
]
}
{ type = "label" name = "uptime" label = "Uptime: {{30000:uptime -p | cut -d ' ' -f2-}}" }
+ { type = "clock" disable_popup = true }
]
}
diff --git a/src/bar.rs b/src/bar.rs
index d91e0fc0..4363806c 100644
--- a/src/bar.rs
+++ b/src/bar.rs
@@ -1,7 +1,5 @@
use crate::config::{BarConfig, BarPosition, MarginConfig, ModuleConfig};
-use crate::modules::{
- create_module, set_widget_identifiers, wrap_widget, ModuleInfo, ModuleLocation,
-};
+use crate::modules::{BarModuleFactory, ModuleInfo, ModuleLocation};
use crate::popup::Popup;
use crate::Ironbar;
use color_eyre::Result;
@@ -350,55 +348,10 @@ fn add_modules(
ironbar: &Rc,
popup: &Rc,
) -> Result<()> {
- let orientation = info.bar_position.orientation();
-
- macro_rules! add_module {
- ($module:expr, $id:expr) => {{
- let common = $module.common.take().expect("common config to exist");
- let widget_parts = create_module(
- *$module,
- $id,
- ironbar.clone(),
- common.name.clone(),
- &info,
- &Rc::clone(&popup),
- )?;
- set_widget_identifiers(&widget_parts, &common);
-
- let container = wrap_widget(&widget_parts.widget, common, orientation);
- content.add(&container);
- }};
- }
+ let module_factory = BarModuleFactory::new(ironbar.clone(), popup.clone()).into();
for config in modules {
- let id = Ironbar::unique_id();
- match config {
- #[cfg(feature = "clipboard")]
- ModuleConfig::Clipboard(mut module) => add_module!(module, id),
- #[cfg(feature = "clock")]
- ModuleConfig::Clock(mut module) => add_module!(module, id),
- ModuleConfig::Custom(mut module) => add_module!(module, id),
- #[cfg(feature = "focused")]
- ModuleConfig::Focused(mut module) => add_module!(module, id),
- ModuleConfig::Label(mut module) => add_module!(module, id),
- #[cfg(feature = "launcher")]
- ModuleConfig::Launcher(mut module) => add_module!(module, id),
- #[cfg(feature = "music")]
- ModuleConfig::Music(mut module) => add_module!(module, id),
- #[cfg(feature = "notifications")]
- ModuleConfig::Notifications(mut module) => add_module!(module, id),
- ModuleConfig::Script(mut module) => add_module!(module, id),
- #[cfg(feature = "sys_info")]
- ModuleConfig::SysInfo(mut module) => add_module!(module, id),
- #[cfg(feature = "tray")]
- ModuleConfig::Tray(mut module) => add_module!(module, id),
- #[cfg(feature = "upower")]
- ModuleConfig::Upower(mut module) => add_module!(module, id),
- #[cfg(feature = "volume")]
- ModuleConfig::Volume(mut module) => add_module!(module, id),
- #[cfg(feature = "workspaces")]
- ModuleConfig::Workspaces(mut module) => add_module!(module, id),
- }
+ config.create(&module_factory, content, info)?;
}
Ok(())
diff --git a/src/config/common.rs b/src/config/common.rs
index 3e3cc850..fd207cfd 100644
--- a/src/config/common.rs
+++ b/src/config/common.rs
@@ -27,6 +27,7 @@ pub struct CommonConfig {
pub on_mouse_exit: Option,
pub tooltip: Option,
+ pub disable_popup: bool,
}
#[derive(Debug, Deserialize, Clone)]
diff --git a/src/config/mod.rs b/src/config/mod.rs
index a20572bc..52fcb8d6 100644
--- a/src/config/mod.rs
+++ b/src/config/mod.rs
@@ -27,7 +27,10 @@ use crate::modules::upower::UpowerModule;
use crate::modules::volume::VolumeModule;
#[cfg(feature = "workspaces")]
use crate::modules::workspaces::WorkspacesModule;
+
+use crate::modules::{AnyModuleFactory, ModuleFactory, ModuleInfo};
use cfg_if::cfg_if;
+use color_eyre::Result;
use serde::Deserialize;
use std::collections::HashMap;
@@ -64,6 +67,49 @@ pub enum ModuleConfig {
Workspaces(Box),
}
+impl ModuleConfig {
+ pub fn create(
+ self,
+ module_factory: &AnyModuleFactory,
+ container: >k::Box,
+ info: &ModuleInfo,
+ ) -> Result<()> {
+ macro_rules! create {
+ ($module:expr) => {
+ module_factory.create(*$module, container, info)
+ };
+ }
+
+ match self {
+ #[cfg(feature = "clipboard")]
+ Self::Clipboard(module) => create!(module),
+ #[cfg(feature = "clock")]
+ Self::Clock(module) => create!(module),
+ Self::Custom(module) => create!(module),
+ #[cfg(feature = "focused")]
+ Self::Focused(module) => create!(module),
+ Self::Label(module) => create!(module),
+ #[cfg(feature = "launcher")]
+ Self::Launcher(module) => create!(module),
+ #[cfg(feature = "music")]
+ Self::Music(module) => create!(module),
+ #[cfg(feature = "notifications")]
+ Self::Notifications(module) => create!(module),
+ Self::Script(module) => create!(module),
+ #[cfg(feature = "sys_info")]
+ Self::SysInfo(module) => create!(module),
+ #[cfg(feature = "tray")]
+ Self::Tray(module) => create!(module),
+ #[cfg(feature = "upower")]
+ Self::Upower(module) => create!(module),
+ #[cfg(feature = "volume")]
+ Self::Volume(module) => create!(module),
+ #[cfg(feature = "workspaces")]
+ Self::Workspaces(module) => create!(module),
+ }
+ }
+}
+
#[derive(Debug, Deserialize, Clone)]
pub enum BarEntryConfig {
Single(BarConfig),
diff --git a/src/ipc/server.rs b/src/ipc/server.rs
index 931a4fa8..776efe1d 100644
--- a/src/ipc/server.rs
+++ b/src/ipc/server.rs
@@ -172,7 +172,7 @@ impl Ipc {
popup.hide();
let data = popup
- .cache
+ .container_cache
.borrow()
.iter()
.find(|(_, value)| value.name == name)
@@ -209,7 +209,7 @@ impl Ipc {
popup.hide();
let data = popup
- .cache
+ .container_cache
.borrow()
.iter()
.find(|(_, value)| value.name == name)
diff --git a/src/macros.rs b/src/macros.rs
index e5fd195e..cabc8a02 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -1,3 +1,31 @@
+/// Provides implementations of methods required by the `Module` trait
+/// which cannot be included as part of the trait.
+///
+/// This removes the need to add the same boilerplate method definitions
+/// to every module implementation.
+///
+/// # Usage:
+///
+/// ```rs
+/// impl Module for ClockModule {
+/// type SendMessage = DateTime;
+/// type ReceiveMessage = ();
+///
+/// module_impl!("clock");
+/// }
+#[macro_export]
+macro_rules! module_impl {
+ ($name:literal) => {
+ fn name() -> &'static str {
+ $name
+ }
+
+ fn take_common(&mut self) -> $crate::config::CommonConfig {
+ self.common.take().expect("common config to exist")
+ }
+ };
+}
+
/// Sends a message on an asynchronous `Sender` using `send()`
/// Panics if the message cannot be sent.
///
diff --git a/src/modules/clipboard.rs b/src/modules/clipboard.rs
index 9749df68..6d53e401 100644
--- a/src/modules/clipboard.rs
+++ b/src/modules/clipboard.rs
@@ -5,7 +5,7 @@ use crate::image::new_icon_button;
use crate::modules::{
Module, ModuleInfo, ModuleParts, ModulePopup, ModuleUpdateEvent, PopupButton, WidgetContext,
};
-use crate::{glib_recv, spawn, try_send};
+use crate::{glib_recv, module_impl, spawn, try_send};
use glib::Propagation;
use gtk::gdk_pixbuf::Pixbuf;
use gtk::gio::{Cancellable, MemoryInputStream};
@@ -65,9 +65,7 @@ impl Module