mirror of
https://github.com/Zedfrigg/ironbar.git
synced 2025-07-01 18:51:04 +02:00
feat: ability to add custom modules instead native modules
Resolves #131
This commit is contained in:
parent
42ae915645
commit
994f4a4a12
21 changed files with 497 additions and 252 deletions
|
@ -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.
|
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,
|
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.
|
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.
|
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.
|
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`
|
### `Widget`
|
||||||
|
|
||||||
There are many widget types, each with their own config options.
|
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 |
|
| Name | Type | Default | Description |
|
||||||
|---------------|------------------------------------------------------------|----------------|-------------------------------------------------------------------|
|
|---------------|------------------------------------------------------------|----------------|-------------------------------------------------------------------|
|
||||||
| `orientation` | `'horizontal'` or `'vertical'` (shorthand: `'h'` or `'v'`) | `'horizontal'` | Whether child widgets should be horizontally or vertically added. |
|
| `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
|
#### Label
|
||||||
|
|
||||||
|
|
53
src/bar.rs
53
src/bar.rs
|
@ -1,7 +1,5 @@
|
||||||
use crate::config::{BarConfig, BarPosition, MarginConfig, ModuleConfig};
|
use crate::config::{BarConfig, BarPosition, MarginConfig, ModuleConfig};
|
||||||
use crate::modules::{
|
use crate::modules::{BarModuleFactory, ModuleInfo, ModuleLocation};
|
||||||
create_module, set_widget_identifiers, wrap_widget, ModuleInfo, ModuleLocation,
|
|
||||||
};
|
|
||||||
use crate::popup::Popup;
|
use crate::popup::Popup;
|
||||||
use crate::Ironbar;
|
use crate::Ironbar;
|
||||||
use color_eyre::Result;
|
use color_eyre::Result;
|
||||||
|
@ -350,55 +348,10 @@ fn add_modules(
|
||||||
ironbar: &Rc<Ironbar>,
|
ironbar: &Rc<Ironbar>,
|
||||||
popup: &Rc<Popup>,
|
popup: &Rc<Popup>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let orientation = info.bar_position.orientation();
|
let module_factory = BarModuleFactory::new(ironbar.clone(), popup.clone()).into();
|
||||||
|
|
||||||
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);
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
for config in modules {
|
for config in modules {
|
||||||
let id = Ironbar::unique_id();
|
config.create(&module_factory, content, info)?;
|
||||||
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),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -27,7 +27,10 @@ use crate::modules::upower::UpowerModule;
|
||||||
use crate::modules::volume::VolumeModule;
|
use crate::modules::volume::VolumeModule;
|
||||||
#[cfg(feature = "workspaces")]
|
#[cfg(feature = "workspaces")]
|
||||||
use crate::modules::workspaces::WorkspacesModule;
|
use crate::modules::workspaces::WorkspacesModule;
|
||||||
|
|
||||||
|
use crate::modules::{AnyModuleFactory, ModuleFactory, ModuleInfo};
|
||||||
use cfg_if::cfg_if;
|
use cfg_if::cfg_if;
|
||||||
|
use color_eyre::Result;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
@ -64,6 +67,49 @@ pub enum ModuleConfig {
|
||||||
Workspaces(Box<WorkspacesModule>),
|
Workspaces(Box<WorkspacesModule>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
pub enum BarEntryConfig {
|
pub enum BarEntryConfig {
|
||||||
Single(BarConfig),
|
Single(BarConfig),
|
||||||
|
|
|
@ -172,7 +172,7 @@ impl Ipc {
|
||||||
popup.hide();
|
popup.hide();
|
||||||
|
|
||||||
let data = popup
|
let data = popup
|
||||||
.cache
|
.container_cache
|
||||||
.borrow()
|
.borrow()
|
||||||
.iter()
|
.iter()
|
||||||
.find(|(_, value)| value.name == name)
|
.find(|(_, value)| value.name == name)
|
||||||
|
@ -209,7 +209,7 @@ impl Ipc {
|
||||||
popup.hide();
|
popup.hide();
|
||||||
|
|
||||||
let data = popup
|
let data = popup
|
||||||
.cache
|
.container_cache
|
||||||
.borrow()
|
.borrow()
|
||||||
.iter()
|
.iter()
|
||||||
.find(|(_, value)| value.name == name)
|
.find(|(_, value)| value.name == name)
|
||||||
|
|
|
@ -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<Local>;
|
||||||
|
/// 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()`
|
/// Sends a message on an asynchronous `Sender` using `send()`
|
||||||
/// Panics if the message cannot be sent.
|
/// Panics if the message cannot be sent.
|
||||||
///
|
///
|
||||||
|
|
|
@ -5,7 +5,7 @@ use crate::image::new_icon_button;
|
||||||
use crate::modules::{
|
use crate::modules::{
|
||||||
Module, ModuleInfo, ModuleParts, ModulePopup, ModuleUpdateEvent, PopupButton, WidgetContext,
|
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 glib::Propagation;
|
||||||
use gtk::gdk_pixbuf::Pixbuf;
|
use gtk::gdk_pixbuf::Pixbuf;
|
||||||
use gtk::gio::{Cancellable, MemoryInputStream};
|
use gtk::gio::{Cancellable, MemoryInputStream};
|
||||||
|
@ -65,9 +65,7 @@ impl Module<Button> for ClipboardModule {
|
||||||
type SendMessage = ControllerEvent;
|
type SendMessage = ControllerEvent;
|
||||||
type ReceiveMessage = UIEvent;
|
type ReceiveMessage = UIEvent;
|
||||||
|
|
||||||
fn name() -> &'static str {
|
module_impl!("clipboard");
|
||||||
"clipboard"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn spawn_controller(
|
fn spawn_controller(
|
||||||
&self,
|
&self,
|
||||||
|
@ -137,7 +135,7 @@ impl Module<Button> for ClipboardModule {
|
||||||
|
|
||||||
let rx = context.subscribe();
|
let rx = context.subscribe();
|
||||||
let popup = self
|
let popup = self
|
||||||
.into_popup(context.controller_tx, rx, info)
|
.into_popup(context.controller_tx.clone(), rx, context, info)
|
||||||
.into_popup_parts(vec![&button]);
|
.into_popup_parts(vec![&button]);
|
||||||
|
|
||||||
Ok(ModuleParts::new(button, popup))
|
Ok(ModuleParts::new(button, popup))
|
||||||
|
@ -147,6 +145,7 @@ impl Module<Button> for ClipboardModule {
|
||||||
self,
|
self,
|
||||||
tx: mpsc::Sender<Self::ReceiveMessage>,
|
tx: mpsc::Sender<Self::ReceiveMessage>,
|
||||||
rx: broadcast::Receiver<Self::SendMessage>,
|
rx: broadcast::Receiver<Self::SendMessage>,
|
||||||
|
_context: WidgetContext<Self::SendMessage, Self::ReceiveMessage>,
|
||||||
_info: &ModuleInfo,
|
_info: &ModuleInfo,
|
||||||
) -> Option<gtk::Box>
|
) -> Option<gtk::Box>
|
||||||
where
|
where
|
||||||
|
|
|
@ -13,7 +13,7 @@ use crate::gtk_helpers::IronbarGtkExt;
|
||||||
use crate::modules::{
|
use crate::modules::{
|
||||||
Module, ModuleInfo, ModuleParts, ModulePopup, ModuleUpdateEvent, PopupButton, WidgetContext,
|
Module, ModuleInfo, ModuleParts, ModulePopup, ModuleUpdateEvent, PopupButton, WidgetContext,
|
||||||
};
|
};
|
||||||
use crate::{glib_recv, send_async, spawn, try_send};
|
use crate::{glib_recv, module_impl, send_async, spawn, try_send};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
pub struct ClockModule {
|
pub struct ClockModule {
|
||||||
|
@ -71,9 +71,7 @@ impl Module<Button> for ClockModule {
|
||||||
type SendMessage = DateTime<Local>;
|
type SendMessage = DateTime<Local>;
|
||||||
type ReceiveMessage = ();
|
type ReceiveMessage = ();
|
||||||
|
|
||||||
fn name() -> &'static str {
|
module_impl!("clock");
|
||||||
"clock"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn spawn_controller(
|
fn spawn_controller(
|
||||||
&self,
|
&self,
|
||||||
|
@ -120,7 +118,12 @@ impl Module<Button> for ClockModule {
|
||||||
});
|
});
|
||||||
|
|
||||||
let popup = self
|
let popup = self
|
||||||
.into_popup(context.controller_tx.clone(), context.subscribe(), info)
|
.into_popup(
|
||||||
|
context.controller_tx.clone(),
|
||||||
|
context.subscribe(),
|
||||||
|
context,
|
||||||
|
info,
|
||||||
|
)
|
||||||
.into_popup_parts(vec![&button]);
|
.into_popup_parts(vec![&button]);
|
||||||
|
|
||||||
Ok(ModuleParts::new(button, popup))
|
Ok(ModuleParts::new(button, popup))
|
||||||
|
@ -130,6 +133,7 @@ impl Module<Button> for ClockModule {
|
||||||
self,
|
self,
|
||||||
_tx: mpsc::Sender<Self::ReceiveMessage>,
|
_tx: mpsc::Sender<Self::ReceiveMessage>,
|
||||||
rx: broadcast::Receiver<Self::SendMessage>,
|
rx: broadcast::Receiver<Self::SendMessage>,
|
||||||
|
_context: WidgetContext<Self::SendMessage, Self::ReceiveMessage>,
|
||||||
_info: &ModuleInfo,
|
_info: &ModuleInfo,
|
||||||
) -> Option<gtk::Box> {
|
) -> Option<gtk::Box> {
|
||||||
let container = gtk::Box::new(Orientation::Vertical, 0);
|
let container = gtk::Box::new(Orientation::Vertical, 0);
|
||||||
|
|
|
@ -9,14 +9,15 @@ use self::image::ImageWidget;
|
||||||
use self::label::LabelWidget;
|
use self::label::LabelWidget;
|
||||||
use self::r#box::BoxWidget;
|
use self::r#box::BoxWidget;
|
||||||
use self::slider::SliderWidget;
|
use self::slider::SliderWidget;
|
||||||
use crate::config::CommonConfig;
|
use crate::config::{CommonConfig, ModuleConfig};
|
||||||
use crate::modules::custom::button::ButtonWidget;
|
use crate::modules::custom::button::ButtonWidget;
|
||||||
use crate::modules::custom::progress::ProgressWidget;
|
use crate::modules::custom::progress::ProgressWidget;
|
||||||
use crate::modules::{
|
use crate::modules::{
|
||||||
wrap_widget, Module, ModuleInfo, ModuleParts, ModulePopup, ModuleUpdateEvent, WidgetContext,
|
wrap_widget, AnyModuleFactory, BarModuleFactory, Module, ModuleInfo, ModuleParts, ModulePopup,
|
||||||
|
ModuleUpdateEvent, PopupButton, PopupModuleFactory, WidgetContext,
|
||||||
};
|
};
|
||||||
use crate::script::Script;
|
use crate::script::Script;
|
||||||
use crate::{send_async, spawn};
|
use crate::{module_impl, send_async, spawn};
|
||||||
use color_eyre::{Report, Result};
|
use color_eyre::{Report, Result};
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
use gtk::{Button, IconTheme, Orientation};
|
use gtk::{Button, IconTheme, Orientation};
|
||||||
|
@ -40,11 +41,18 @@ pub struct CustomModule {
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
pub struct WidgetConfig {
|
pub struct WidgetConfig {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
widget: Widget,
|
widget: WidgetOrModule,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
common: CommonConfig,
|
common: CommonConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum WidgetOrModule {
|
||||||
|
Widget(Widget),
|
||||||
|
Module(ModuleConfig),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
#[serde(tag = "type", rename_all = "snake_case")]
|
#[serde(tag = "type", rename_all = "snake_case")]
|
||||||
pub enum Widget {
|
pub enum Widget {
|
||||||
|
@ -58,10 +66,12 @@ pub enum Widget {
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct CustomWidgetContext<'a> {
|
struct CustomWidgetContext<'a> {
|
||||||
|
info: &'a ModuleInfo<'a>,
|
||||||
tx: &'a mpsc::Sender<ExecEvent>,
|
tx: &'a mpsc::Sender<ExecEvent>,
|
||||||
bar_orientation: Orientation,
|
bar_orientation: Orientation,
|
||||||
icon_theme: &'a IconTheme,
|
icon_theme: &'a IconTheme,
|
||||||
popup_buttons: Rc<RefCell<Vec<Button>>>,
|
popup_buttons: Rc<RefCell<Vec<Button>>>,
|
||||||
|
module_factory: AnyModuleFactory,
|
||||||
}
|
}
|
||||||
|
|
||||||
trait CustomWidget {
|
trait CustomWidget {
|
||||||
|
@ -114,6 +124,19 @@ fn try_get_orientation(orientation: &str) -> Result<Orientation> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl WidgetOrModule {
|
||||||
|
fn add_to(self, parent: >k::Box, context: &CustomWidgetContext, common: CommonConfig) {
|
||||||
|
match self {
|
||||||
|
WidgetOrModule::Widget(widget) => widget.add_to(parent, context, common),
|
||||||
|
WidgetOrModule::Module(config) => {
|
||||||
|
if let Err(err) = config.create(&context.module_factory, parent, context.info) {
|
||||||
|
error!("{err:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Widget {
|
impl Widget {
|
||||||
/// Creates this widget and adds it to the parent container
|
/// Creates this widget and adds it to the parent container
|
||||||
fn add_to(self, parent: >k::Box, context: &CustomWidgetContext, common: CommonConfig) {
|
fn add_to(self, parent: >k::Box, context: &CustomWidgetContext, common: CommonConfig) {
|
||||||
|
@ -151,9 +174,7 @@ impl Module<gtk::Box> for CustomModule {
|
||||||
type SendMessage = ();
|
type SendMessage = ();
|
||||||
type ReceiveMessage = ExecEvent;
|
type ReceiveMessage = ExecEvent;
|
||||||
|
|
||||||
fn name() -> &'static str {
|
module_impl!("custom");
|
||||||
"custom"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn spawn_controller(
|
fn spawn_controller(
|
||||||
&self,
|
&self,
|
||||||
|
@ -191,7 +212,7 @@ impl Module<gtk::Box> for CustomModule {
|
||||||
|
|
||||||
fn into_widget(
|
fn into_widget(
|
||||||
self,
|
self,
|
||||||
context: WidgetContext<Self::SendMessage, Self::ReceiveMessage>,
|
mut context: WidgetContext<Self::SendMessage, Self::ReceiveMessage>,
|
||||||
info: &ModuleInfo,
|
info: &ModuleInfo,
|
||||||
) -> Result<ModuleParts<gtk::Box>> {
|
) -> Result<ModuleParts<gtk::Box>> {
|
||||||
let orientation = info.bar_position.orientation();
|
let orientation = info.bar_position.orientation();
|
||||||
|
@ -200,10 +221,13 @@ impl Module<gtk::Box> for CustomModule {
|
||||||
let popup_buttons = Rc::new(RefCell::new(Vec::new()));
|
let popup_buttons = Rc::new(RefCell::new(Vec::new()));
|
||||||
|
|
||||||
let custom_context = CustomWidgetContext {
|
let custom_context = CustomWidgetContext {
|
||||||
|
info,
|
||||||
tx: &context.controller_tx,
|
tx: &context.controller_tx,
|
||||||
bar_orientation: orientation,
|
bar_orientation: orientation,
|
||||||
icon_theme: info.icon_theme,
|
icon_theme: info.icon_theme,
|
||||||
popup_buttons: popup_buttons.clone(),
|
popup_buttons: popup_buttons.clone(),
|
||||||
|
module_factory: BarModuleFactory::new(context.ironbar.clone(), context.popup.clone())
|
||||||
|
.into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.bar.clone().into_iter().for_each(|widget| {
|
self.bar.clone().into_iter().for_each(|widget| {
|
||||||
|
@ -212,8 +236,22 @@ impl Module<gtk::Box> for CustomModule {
|
||||||
.add_to(&container, &custom_context, widget.common);
|
.add_to(&container, &custom_context, widget.common);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
for button in popup_buttons.borrow().iter() {
|
||||||
|
button.ensure_popup_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
context.button_id = popup_buttons
|
||||||
|
.borrow()
|
||||||
|
.first()
|
||||||
|
.map_or(usize::MAX, |button| button.popup_id());
|
||||||
|
|
||||||
let popup = self
|
let popup = self
|
||||||
.into_popup(context.controller_tx.clone(), context.subscribe(), info)
|
.into_popup(
|
||||||
|
context.controller_tx.clone(),
|
||||||
|
context.subscribe(),
|
||||||
|
context,
|
||||||
|
info,
|
||||||
|
)
|
||||||
.into_popup_parts_owned(popup_buttons.take());
|
.into_popup_parts_owned(popup_buttons.take());
|
||||||
|
|
||||||
Ok(ModuleParts {
|
Ok(ModuleParts {
|
||||||
|
@ -226,6 +264,7 @@ impl Module<gtk::Box> for CustomModule {
|
||||||
self,
|
self,
|
||||||
tx: mpsc::Sender<Self::ReceiveMessage>,
|
tx: mpsc::Sender<Self::ReceiveMessage>,
|
||||||
_rx: broadcast::Receiver<Self::SendMessage>,
|
_rx: broadcast::Receiver<Self::SendMessage>,
|
||||||
|
context: WidgetContext<Self::SendMessage, Self::ReceiveMessage>,
|
||||||
info: &ModuleInfo,
|
info: &ModuleInfo,
|
||||||
) -> Option<gtk::Box>
|
) -> Option<gtk::Box>
|
||||||
where
|
where
|
||||||
|
@ -235,10 +274,17 @@ impl Module<gtk::Box> for CustomModule {
|
||||||
|
|
||||||
if let Some(popup) = self.popup {
|
if let Some(popup) = self.popup {
|
||||||
let custom_context = CustomWidgetContext {
|
let custom_context = CustomWidgetContext {
|
||||||
|
info,
|
||||||
tx: &tx,
|
tx: &tx,
|
||||||
bar_orientation: info.bar_position.orientation(),
|
bar_orientation: info.bar_position.orientation(),
|
||||||
icon_theme: info.icon_theme,
|
icon_theme: info.icon_theme,
|
||||||
popup_buttons: Rc::new(RefCell::new(vec![])),
|
popup_buttons: Rc::new(RefCell::new(vec![])),
|
||||||
|
module_factory: PopupModuleFactory::new(
|
||||||
|
context.ironbar,
|
||||||
|
context.popup,
|
||||||
|
context.button_id,
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
for widget in popup {
|
for widget in popup {
|
||||||
|
|
|
@ -3,7 +3,7 @@ use crate::config::{CommonConfig, TruncateMode};
|
||||||
use crate::gtk_helpers::IronbarGtkExt;
|
use crate::gtk_helpers::IronbarGtkExt;
|
||||||
use crate::image::ImageProvider;
|
use crate::image::ImageProvider;
|
||||||
use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext};
|
use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext};
|
||||||
use crate::{glib_recv, send_async, spawn, try_send};
|
use crate::{glib_recv, module_impl, send_async, spawn, try_send};
|
||||||
use color_eyre::Result;
|
use color_eyre::Result;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
use gtk::Label;
|
use gtk::Label;
|
||||||
|
@ -50,9 +50,7 @@ impl Module<gtk::Box> for FocusedModule {
|
||||||
type SendMessage = Option<(String, String)>;
|
type SendMessage = Option<(String, String)>;
|
||||||
type ReceiveMessage = ();
|
type ReceiveMessage = ();
|
||||||
|
|
||||||
fn name() -> &'static str {
|
module_impl!("focused");
|
||||||
"focused"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn spawn_controller(
|
fn spawn_controller(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::config::CommonConfig;
|
use crate::config::CommonConfig;
|
||||||
use crate::dynamic_value::dynamic_string;
|
use crate::dynamic_value::dynamic_string;
|
||||||
use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext};
|
use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext};
|
||||||
use crate::{glib_recv, try_send};
|
use crate::{glib_recv, module_impl, try_send};
|
||||||
use color_eyre::Result;
|
use color_eyre::Result;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
use gtk::Label;
|
use gtk::Label;
|
||||||
|
@ -29,9 +29,7 @@ impl Module<Label> for LabelModule {
|
||||||
type SendMessage = String;
|
type SendMessage = String;
|
||||||
type ReceiveMessage = ();
|
type ReceiveMessage = ();
|
||||||
|
|
||||||
fn name() -> &'static str {
|
module_impl!("label");
|
||||||
"label"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn spawn_controller(
|
fn spawn_controller(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
@ -7,7 +7,7 @@ use super::{Module, ModuleInfo, ModuleParts, ModulePopup, ModuleUpdateEvent, Wid
|
||||||
use crate::clients::wayland::{self, ToplevelEvent};
|
use crate::clients::wayland::{self, ToplevelEvent};
|
||||||
use crate::config::CommonConfig;
|
use crate::config::CommonConfig;
|
||||||
use crate::desktop_file::find_desktop_file;
|
use crate::desktop_file::find_desktop_file;
|
||||||
use crate::{arc_mut, glib_recv, lock, send_async, spawn, try_send, write_lock};
|
use crate::{arc_mut, glib_recv, lock, module_impl, send_async, spawn, try_send, write_lock};
|
||||||
use color_eyre::{Help, Report};
|
use color_eyre::{Help, Report};
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
use gtk::{Button, Orientation};
|
use gtk::{Button, Orientation};
|
||||||
|
@ -80,9 +80,7 @@ impl Module<gtk::Box> for LauncherModule {
|
||||||
type SendMessage = LauncherUpdate;
|
type SendMessage = LauncherUpdate;
|
||||||
type ReceiveMessage = ItemEvent;
|
type ReceiveMessage = ItemEvent;
|
||||||
|
|
||||||
fn name() -> &'static str {
|
module_impl!("launcher");
|
||||||
"launcher"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn spawn_controller(
|
fn spawn_controller(
|
||||||
&self,
|
&self,
|
||||||
|
@ -401,7 +399,7 @@ impl Module<gtk::Box> for LauncherModule {
|
||||||
|
|
||||||
let rx = context.subscribe();
|
let rx = context.subscribe();
|
||||||
let popup = self
|
let popup = self
|
||||||
.into_popup(context.controller_tx, rx, info)
|
.into_popup(context.controller_tx.clone(), rx, context, info)
|
||||||
.into_popup_parts(vec![]); // since item buttons are dynamic, they pass their geometry directly
|
.into_popup_parts(vec![]); // since item buttons are dynamic, they pass their geometry directly
|
||||||
|
|
||||||
Ok(ModuleParts {
|
Ok(ModuleParts {
|
||||||
|
@ -414,6 +412,7 @@ impl Module<gtk::Box> for LauncherModule {
|
||||||
self,
|
self,
|
||||||
controller_tx: mpsc::Sender<Self::ReceiveMessage>,
|
controller_tx: mpsc::Sender<Self::ReceiveMessage>,
|
||||||
rx: broadcast::Receiver<Self::SendMessage>,
|
rx: broadcast::Receiver<Self::SendMessage>,
|
||||||
|
_context: WidgetContext<Self::SendMessage, Self::ReceiveMessage>,
|
||||||
_info: &ModuleInfo,
|
_info: &ModuleInfo,
|
||||||
) -> Option<gtk::Box> {
|
) -> Option<gtk::Box> {
|
||||||
const MAX_WIDTH: i32 = 250;
|
const MAX_WIDTH: i32 = 250;
|
||||||
|
|
|
@ -54,6 +54,8 @@ pub enum ModuleLocation {
|
||||||
Center,
|
Center,
|
||||||
Right,
|
Right,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct ModuleInfo<'a> {
|
pub struct ModuleInfo<'a> {
|
||||||
pub app: &'a Application,
|
pub app: &'a Application,
|
||||||
pub location: ModuleLocation,
|
pub location: ModuleLocation,
|
||||||
|
@ -85,10 +87,16 @@ where
|
||||||
{
|
{
|
||||||
pub id: usize,
|
pub id: usize,
|
||||||
pub ironbar: Rc<Ironbar>,
|
pub ironbar: Rc<Ironbar>,
|
||||||
|
pub popup: Rc<Popup>,
|
||||||
pub tx: mpsc::Sender<ModuleUpdateEvent<TSend>>,
|
pub tx: mpsc::Sender<ModuleUpdateEvent<TSend>>,
|
||||||
pub update_tx: broadcast::Sender<TSend>,
|
pub update_tx: broadcast::Sender<TSend>,
|
||||||
pub controller_tx: mpsc::Sender<TReceive>,
|
pub controller_tx: mpsc::Sender<TReceive>,
|
||||||
|
|
||||||
|
// TODO: Don't like this - need some serious refactoring to deal with it
|
||||||
|
// This is a hack to be able to pass data from module -> popup creation
|
||||||
|
// for custom widget only.
|
||||||
|
pub button_id: usize,
|
||||||
|
|
||||||
_update_rx: broadcast::Receiver<TSend>,
|
_update_rx: broadcast::Receiver<TSend>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,6 +130,32 @@ impl<W: IsA<Widget>> ModuleParts<W> {
|
||||||
fn new(widget: W, popup: Option<ModulePopupParts>) -> Self {
|
fn new(widget: W, popup: Option<ModulePopupParts>) -> Self {
|
||||||
Self { widget, popup }
|
Self { widget, popup }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn setup_identifiers(&self, common: &CommonConfig) {
|
||||||
|
if let Some(ref name) = common.name {
|
||||||
|
self.widget.set_widget_name(name);
|
||||||
|
|
||||||
|
if let Some(ref popup) = self.popup {
|
||||||
|
popup.container.set_widget_name(&format!("popup-{name}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ref class) = common.class {
|
||||||
|
// gtk counts classes with spaces as the same class
|
||||||
|
for part in class.split(' ') {
|
||||||
|
self.widget.style_context().add_class(part);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ref popup) = self.popup {
|
||||||
|
for part in class.split(' ') {
|
||||||
|
popup
|
||||||
|
.container
|
||||||
|
.style_context()
|
||||||
|
.add_class(&format!("popup-{part}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -150,11 +184,24 @@ impl ModulePopup for Option<gtk::Box> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait PopupButton {
|
pub trait PopupButton {
|
||||||
|
fn ensure_popup_id(&self) -> usize;
|
||||||
fn try_popup_id(&self) -> Option<usize>;
|
fn try_popup_id(&self) -> Option<usize>;
|
||||||
fn popup_id(&self) -> usize;
|
fn popup_id(&self) -> usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PopupButton for Button {
|
impl PopupButton for Button {
|
||||||
|
/// Gets the popup ID associated with this button,
|
||||||
|
/// or creates a new one if it does not exist.
|
||||||
|
fn ensure_popup_id(&self) -> usize {
|
||||||
|
if let Some(id) = self.try_popup_id() {
|
||||||
|
id
|
||||||
|
} else {
|
||||||
|
let id = Ironbar::unique_id();
|
||||||
|
self.set_tag("popup-id", id);
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets the popup ID associated with this button, if there is one.
|
/// Gets the popup ID associated with this button, if there is one.
|
||||||
/// Will return `None` if this is not a popup button.
|
/// Will return `None` if this is not a popup button.
|
||||||
fn try_popup_id(&self) -> Option<usize> {
|
fn try_popup_id(&self) -> Option<usize> {
|
||||||
|
@ -201,48 +248,57 @@ where
|
||||||
self,
|
self,
|
||||||
_tx: mpsc::Sender<Self::ReceiveMessage>,
|
_tx: mpsc::Sender<Self::ReceiveMessage>,
|
||||||
_rx: broadcast::Receiver<Self::SendMessage>,
|
_rx: broadcast::Receiver<Self::SendMessage>,
|
||||||
|
_context: WidgetContext<Self::SendMessage, Self::ReceiveMessage>,
|
||||||
_info: &ModuleInfo,
|
_info: &ModuleInfo,
|
||||||
) -> Option<gtk::Box>
|
) -> Option<gtk::Box>
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
|
<Self as Module<W>>::SendMessage: Clone,
|
||||||
{
|
{
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn take_common(&mut self) -> CommonConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a module and sets it up.
|
pub trait ModuleFactory {
|
||||||
/// This setup includes widget/popup content and event channels.
|
fn create<TModule, TWidget, TSend, TRev>(
|
||||||
pub fn create_module<TModule, TWidget, TSend, TRec>(
|
&self,
|
||||||
module: TModule,
|
mut module: TModule,
|
||||||
id: usize,
|
container: >k::Box,
|
||||||
ironbar: Rc<Ironbar>,
|
|
||||||
name: Option<String>,
|
|
||||||
info: &ModuleInfo,
|
info: &ModuleInfo,
|
||||||
popup: &Rc<Popup>,
|
) -> Result<()>
|
||||||
) -> Result<ModuleParts<TWidget>>
|
where
|
||||||
where
|
TModule: Module<TWidget, SendMessage = TSend, ReceiveMessage = TRev>,
|
||||||
TModule: Module<TWidget, SendMessage = TSend, ReceiveMessage = TRec>,
|
|
||||||
TWidget: IsA<Widget>,
|
TWidget: IsA<Widget>,
|
||||||
TSend: Debug + Clone + Send + 'static,
|
TSend: Debug + Clone + Send + 'static,
|
||||||
{
|
{
|
||||||
|
let id = Ironbar::unique_id();
|
||||||
|
let common = module.take_common();
|
||||||
|
|
||||||
let (ui_tx, ui_rx) = mpsc::channel::<ModuleUpdateEvent<TSend>>(64);
|
let (ui_tx, ui_rx) = mpsc::channel::<ModuleUpdateEvent<TSend>>(64);
|
||||||
let (controller_tx, controller_rx) = mpsc::channel::<TRec>(64);
|
let (controller_tx, controller_rx) = mpsc::channel::<TRev>(64);
|
||||||
|
|
||||||
let (tx, rx) = broadcast::channel(64);
|
let (tx, rx) = broadcast::channel(64);
|
||||||
|
|
||||||
let context = WidgetContext {
|
let context = WidgetContext {
|
||||||
id,
|
id,
|
||||||
ironbar,
|
ironbar: self.ironbar().clone(),
|
||||||
|
popup: self.popup().clone(),
|
||||||
tx: ui_tx,
|
tx: ui_tx,
|
||||||
update_tx: tx.clone(),
|
update_tx: tx.clone(),
|
||||||
controller_tx,
|
controller_tx,
|
||||||
_update_rx: rx,
|
_update_rx: rx,
|
||||||
|
button_id: usize::MAX, // hack :(
|
||||||
};
|
};
|
||||||
|
|
||||||
module.spawn_controller(info, &context, controller_rx)?;
|
module.spawn_controller(info, &context, controller_rx)?;
|
||||||
|
|
||||||
let module_name = TModule::name();
|
let module_name = TModule::name();
|
||||||
let instance_name = name.unwrap_or_else(|| module_name.to_string());
|
let instance_name = common
|
||||||
|
.name
|
||||||
|
.clone()
|
||||||
|
.unwrap_or_else(|| module_name.to_string());
|
||||||
|
|
||||||
let module_parts = module.into_widget(context, info)?;
|
let module_parts = module.into_widget(context, info)?;
|
||||||
module_parts.widget.add_class("widget");
|
module_parts.widget.add_class("widget");
|
||||||
|
@ -254,61 +310,77 @@ where
|
||||||
.style_context()
|
.style_context()
|
||||||
.add_class(&format!("popup-{module_name}"));
|
.add_class(&format!("popup-{module_name}"));
|
||||||
|
|
||||||
popup.register_content(id, instance_name, popup_content);
|
self.popup()
|
||||||
|
.register_content(id, instance_name, popup_content);
|
||||||
}
|
}
|
||||||
|
|
||||||
setup_receiver(tx, ui_rx, popup.clone(), module_name, id);
|
self.setup_receiver(tx, ui_rx, module_name, id);
|
||||||
|
|
||||||
Ok(module_parts)
|
module_parts.setup_identifiers(&common);
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets up the bridge channel receiver
|
let ev_container = wrap_widget(
|
||||||
/// to pick up events from the controller, widget or popup.
|
&module_parts.widget,
|
||||||
///
|
common,
|
||||||
/// Handles opening/closing popups
|
info.bar_position.orientation(),
|
||||||
/// and communicating update messages between controllers and widgets/popups.
|
);
|
||||||
fn setup_receiver<TSend>(
|
container.add(&ev_container);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_receiver<TSend>(
|
||||||
|
&self,
|
||||||
tx: broadcast::Sender<TSend>,
|
tx: broadcast::Sender<TSend>,
|
||||||
rx: mpsc::Receiver<ModuleUpdateEvent<TSend>>,
|
rx: mpsc::Receiver<ModuleUpdateEvent<TSend>>,
|
||||||
popup: Rc<Popup>,
|
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
id: usize,
|
id: usize,
|
||||||
) where
|
) where
|
||||||
TSend: Debug + Clone + Send + 'static,
|
TSend: Debug + Clone + Send + 'static;
|
||||||
{
|
|
||||||
// some rare cases can cause the popup to incorrectly calculate its size on first open.
|
|
||||||
// we can fix that by just force re-rendering it on its first open.
|
|
||||||
let mut has_popup_opened = false;
|
|
||||||
|
|
||||||
|
fn ironbar(&self) -> &Rc<Ironbar>;
|
||||||
|
fn popup(&self) -> &Rc<Popup>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct BarModuleFactory {
|
||||||
|
ironbar: Rc<Ironbar>,
|
||||||
|
popup: Rc<Popup>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BarModuleFactory {
|
||||||
|
pub fn new(ironbar: Rc<Ironbar>, popup: Rc<Popup>) -> Self {
|
||||||
|
Self { ironbar, popup }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModuleFactory for BarModuleFactory {
|
||||||
|
fn setup_receiver<TSend>(
|
||||||
|
&self,
|
||||||
|
tx: broadcast::Sender<TSend>,
|
||||||
|
rx: mpsc::Receiver<ModuleUpdateEvent<TSend>>,
|
||||||
|
name: &'static str,
|
||||||
|
id: usize,
|
||||||
|
) where
|
||||||
|
TSend: Debug + Clone + Send + 'static,
|
||||||
|
{
|
||||||
|
let popup = self.popup.clone();
|
||||||
glib_recv_mpsc!(rx, ev => {
|
glib_recv_mpsc!(rx, ev => {
|
||||||
match ev {
|
match ev {
|
||||||
ModuleUpdateEvent::Update(update) => {
|
ModuleUpdateEvent::Update(update) => {
|
||||||
send!(tx, update);
|
send!(tx, update);
|
||||||
}
|
}
|
||||||
ModuleUpdateEvent::TogglePopup(button_id) => {
|
ModuleUpdateEvent::TogglePopup(button_id) => {
|
||||||
debug!("Toggling popup for {} [#{}]", name, id);
|
debug!("Toggling popup for {} [#{}] (button id: {button_id})", name, id);
|
||||||
if popup.is_visible() {
|
if popup.is_visible() && popup.current_widget().unwrap_or_default() == id {
|
||||||
popup.hide();
|
popup.hide();
|
||||||
} else {
|
} else {
|
||||||
popup.show(id, button_id);
|
popup.show(id, button_id);
|
||||||
|
|
||||||
// force re-render on initial open to try and fix size issue
|
|
||||||
if !has_popup_opened {
|
|
||||||
popup.show(id, button_id);
|
|
||||||
has_popup_opened = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ModuleUpdateEvent::OpenPopup(button_id) => {
|
ModuleUpdateEvent::OpenPopup(button_id) => {
|
||||||
debug!("Opening popup for {} [#{}]", name, id);
|
debug!("Opening popup for {} [#{}] (button id: {button_id})", name, id);
|
||||||
popup.hide();
|
popup.hide();
|
||||||
popup.show(id, button_id);
|
popup.show(id, button_id);
|
||||||
|
|
||||||
// force re-render on initial open to try and fix size issue
|
|
||||||
if !has_popup_opened {
|
|
||||||
popup.show(id, button_id);
|
|
||||||
has_popup_opened = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#[cfg(feature = "launcher")]
|
#[cfg(feature = "launcher")]
|
||||||
ModuleUpdateEvent::OpenPopupAt(geometry) => {
|
ModuleUpdateEvent::OpenPopupAt(geometry) => {
|
||||||
|
@ -316,12 +388,6 @@ fn setup_receiver<TSend>(
|
||||||
|
|
||||||
popup.hide();
|
popup.hide();
|
||||||
popup.show_at(id, geometry);
|
popup.show_at(id, geometry);
|
||||||
|
|
||||||
// force re-render on initial open to try and fix size issue
|
|
||||||
if !has_popup_opened {
|
|
||||||
popup.show_at(id, geometry);
|
|
||||||
has_popup_opened = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ModuleUpdateEvent::ClosePopup => {
|
ModuleUpdateEvent::ClosePopup => {
|
||||||
debug!("Closing popup for {} [#{}]", name, id);
|
debug!("Closing popup for {} [#{}]", name, id);
|
||||||
|
@ -329,34 +395,134 @@ fn setup_receiver<TSend>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ironbar(&self) -> &Rc<Ironbar> {
|
||||||
|
&self.ironbar
|
||||||
|
}
|
||||||
|
|
||||||
|
fn popup(&self) -> &Rc<Popup> {
|
||||||
|
&self.popup
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_widget_identifiers<TWidget: IsA<Widget>>(
|
#[derive(Clone)]
|
||||||
widget_parts: &ModuleParts<TWidget>,
|
pub struct PopupModuleFactory {
|
||||||
common: &CommonConfig,
|
ironbar: Rc<Ironbar>,
|
||||||
) {
|
popup: Rc<Popup>,
|
||||||
if let Some(ref name) = common.name {
|
button_id: usize,
|
||||||
widget_parts.widget.set_widget_name(name);
|
}
|
||||||
|
|
||||||
if let Some(ref popup) = widget_parts.popup {
|
impl PopupModuleFactory {
|
||||||
popup.container.set_widget_name(&format!("popup-{name}"));
|
pub fn new(ironbar: Rc<Ironbar>, popup: Rc<Popup>, button_id: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
ironbar,
|
||||||
|
popup,
|
||||||
|
button_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModuleFactory for PopupModuleFactory {
|
||||||
|
fn setup_receiver<TSend>(
|
||||||
|
&self,
|
||||||
|
tx: broadcast::Sender<TSend>,
|
||||||
|
rx: mpsc::Receiver<ModuleUpdateEvent<TSend>>,
|
||||||
|
name: &'static str,
|
||||||
|
id: usize,
|
||||||
|
) where
|
||||||
|
TSend: Debug + Clone + Send + 'static,
|
||||||
|
{
|
||||||
|
let popup = self.popup.clone();
|
||||||
|
let button_id = self.button_id;
|
||||||
|
glib_recv_mpsc!(rx, ev => {
|
||||||
|
match ev {
|
||||||
|
ModuleUpdateEvent::Update(update) => {
|
||||||
|
send!(tx, update);
|
||||||
|
}
|
||||||
|
ModuleUpdateEvent::TogglePopup(_) => {
|
||||||
|
debug!("Toggling popup for {} [#{}] (button id: {button_id})", name, id);
|
||||||
|
if popup.is_visible() && popup.current_widget().unwrap_or_default() == id {
|
||||||
|
popup.hide();
|
||||||
|
} else {
|
||||||
|
popup.show(id, button_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ModuleUpdateEvent::OpenPopup(_) => {
|
||||||
|
debug!("Opening popup for {} [#{}] (button id: {button_id})", name, id);
|
||||||
|
popup.hide();
|
||||||
|
popup.show(id, button_id);
|
||||||
|
}
|
||||||
|
#[cfg(feature = "launcher")]
|
||||||
|
ModuleUpdateEvent::OpenPopupAt(geometry) => {
|
||||||
|
debug!("Opening popup for {} [#{}]", name, id);
|
||||||
|
|
||||||
|
popup.hide();
|
||||||
|
popup.show_at(id, geometry);
|
||||||
|
}
|
||||||
|
ModuleUpdateEvent::ClosePopup => {
|
||||||
|
debug!("Closing popup for {} [#{}]", name, id);
|
||||||
|
popup.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ironbar(&self) -> &Rc<Ironbar> {
|
||||||
|
&self.ironbar
|
||||||
|
}
|
||||||
|
|
||||||
|
fn popup(&self) -> &Rc<Popup> {
|
||||||
|
&self.popup
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum AnyModuleFactory {
|
||||||
|
Bar(BarModuleFactory),
|
||||||
|
Popup(PopupModuleFactory),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModuleFactory for AnyModuleFactory {
|
||||||
|
fn setup_receiver<TSend>(
|
||||||
|
&self,
|
||||||
|
tx: broadcast::Sender<TSend>,
|
||||||
|
rx: mpsc::Receiver<ModuleUpdateEvent<TSend>>,
|
||||||
|
name: &'static str,
|
||||||
|
id: usize,
|
||||||
|
) where
|
||||||
|
TSend: Debug + Clone + Send + 'static,
|
||||||
|
{
|
||||||
|
match self {
|
||||||
|
AnyModuleFactory::Bar(bar) => bar.setup_receiver(tx, rx, name, id),
|
||||||
|
AnyModuleFactory::Popup(popup) => popup.setup_receiver(tx, rx, name, id),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ref class) = common.class {
|
fn ironbar(&self) -> &Rc<Ironbar> {
|
||||||
// gtk counts classes with spaces as the same class
|
match self {
|
||||||
for part in class.split(' ') {
|
AnyModuleFactory::Bar(bar) => bar.ironbar(),
|
||||||
widget_parts.widget.style_context().add_class(part);
|
AnyModuleFactory::Popup(popup) => popup.ironbar(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ref popup) = widget_parts.popup {
|
fn popup(&self) -> &Rc<Popup> {
|
||||||
for part in class.split(' ') {
|
match self {
|
||||||
popup
|
AnyModuleFactory::Bar(bar) => bar.popup(),
|
||||||
.container
|
AnyModuleFactory::Popup(popup) => popup.popup(),
|
||||||
.style_context()
|
|
||||||
.add_class(&format!("popup-{part}"));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<BarModuleFactory> for AnyModuleFactory {
|
||||||
|
fn from(value: BarModuleFactory) -> Self {
|
||||||
|
Self::Bar(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PopupModuleFactory> for AnyModuleFactory {
|
||||||
|
fn from(value: PopupModuleFactory) -> Self {
|
||||||
|
Self::Popup(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ use crate::modules::PopupButton;
|
||||||
use crate::modules::{
|
use crate::modules::{
|
||||||
Module, ModuleInfo, ModuleParts, ModulePopup, ModuleUpdateEvent, WidgetContext,
|
Module, ModuleInfo, ModuleParts, ModulePopup, ModuleUpdateEvent, WidgetContext,
|
||||||
};
|
};
|
||||||
use crate::{glib_recv, send_async, spawn, try_send};
|
use crate::{glib_recv, module_impl, send_async, spawn, try_send};
|
||||||
|
|
||||||
pub use self::config::MusicModule;
|
pub use self::config::MusicModule;
|
||||||
use self::config::PlayerType;
|
use self::config::PlayerType;
|
||||||
|
@ -87,9 +87,7 @@ impl Module<Button> for MusicModule {
|
||||||
type SendMessage = ControllerEvent;
|
type SendMessage = ControllerEvent;
|
||||||
type ReceiveMessage = PlayerCommand;
|
type ReceiveMessage = PlayerCommand;
|
||||||
|
|
||||||
fn name() -> &'static str {
|
module_impl!("music");
|
||||||
"music"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn spawn_controller(
|
fn spawn_controller(
|
||||||
&self,
|
&self,
|
||||||
|
@ -255,7 +253,7 @@ impl Module<Button> for MusicModule {
|
||||||
|
|
||||||
let rx = context.subscribe();
|
let rx = context.subscribe();
|
||||||
let popup = self
|
let popup = self
|
||||||
.into_popup(context.controller_tx, rx, info)
|
.into_popup(context.controller_tx.clone(), rx, context, info)
|
||||||
.into_popup_parts(vec![&button]);
|
.into_popup_parts(vec![&button]);
|
||||||
|
|
||||||
Ok(ModuleParts::new(button, popup))
|
Ok(ModuleParts::new(button, popup))
|
||||||
|
@ -265,6 +263,7 @@ impl Module<Button> for MusicModule {
|
||||||
self,
|
self,
|
||||||
tx: mpsc::Sender<Self::ReceiveMessage>,
|
tx: mpsc::Sender<Self::ReceiveMessage>,
|
||||||
rx: broadcast::Receiver<Self::SendMessage>,
|
rx: broadcast::Receiver<Self::SendMessage>,
|
||||||
|
_context: WidgetContext<Self::SendMessage, Self::ReceiveMessage>,
|
||||||
info: &ModuleInfo,
|
info: &ModuleInfo,
|
||||||
) -> Option<gtk::Box> {
|
) -> Option<gtk::Box> {
|
||||||
let icon_theme = info.icon_theme;
|
let icon_theme = info.icon_theme;
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::clients::swaync;
|
||||||
use crate::config::CommonConfig;
|
use crate::config::CommonConfig;
|
||||||
use crate::gtk_helpers::IronbarGtkExt;
|
use crate::gtk_helpers::IronbarGtkExt;
|
||||||
use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext};
|
use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext};
|
||||||
use crate::{glib_recv, send_async, spawn, try_send};
|
use crate::{glib_recv, module_impl, send_async, spawn, try_send};
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
use gtk::{Align, Button, Label, Overlay};
|
use gtk::{Align, Button, Label, Overlay};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
@ -97,9 +97,7 @@ impl Module<Overlay> for NotificationsModule {
|
||||||
type SendMessage = swaync::Event;
|
type SendMessage = swaync::Event;
|
||||||
type ReceiveMessage = UiEvent;
|
type ReceiveMessage = UiEvent;
|
||||||
|
|
||||||
fn name() -> &'static str {
|
module_impl!("notifications");
|
||||||
"notifications"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn spawn_controller(
|
fn spawn_controller(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::config::CommonConfig;
|
use crate::config::CommonConfig;
|
||||||
use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext};
|
use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext};
|
||||||
use crate::script::{OutputStream, Script, ScriptMode};
|
use crate::script::{OutputStream, Script, ScriptMode};
|
||||||
use crate::{glib_recv, spawn, try_send};
|
use crate::{glib_recv, module_impl, spawn, try_send};
|
||||||
use color_eyre::{Help, Report, Result};
|
use color_eyre::{Help, Report, Result};
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
use gtk::Label;
|
use gtk::Label;
|
||||||
|
@ -48,9 +48,7 @@ impl Module<Label> for ScriptModule {
|
||||||
type SendMessage = String;
|
type SendMessage = String;
|
||||||
type ReceiveMessage = ();
|
type ReceiveMessage = ();
|
||||||
|
|
||||||
fn name() -> &'static str {
|
module_impl!("script");
|
||||||
"script"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn spawn_controller(
|
fn spawn_controller(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::config::CommonConfig;
|
use crate::config::CommonConfig;
|
||||||
use crate::gtk_helpers::IronbarGtkExt;
|
use crate::gtk_helpers::IronbarGtkExt;
|
||||||
use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext};
|
use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext};
|
||||||
use crate::{glib_recv, send_async, spawn};
|
use crate::{glib_recv, module_impl, send_async, spawn};
|
||||||
use color_eyre::Result;
|
use color_eyre::Result;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
use gtk::Label;
|
use gtk::Label;
|
||||||
|
@ -116,9 +116,7 @@ impl Module<gtk::Box> for SysInfoModule {
|
||||||
type SendMessage = HashMap<String, String>;
|
type SendMessage = HashMap<String, String>;
|
||||||
type ReceiveMessage = ();
|
type ReceiveMessage = ();
|
||||||
|
|
||||||
fn name() -> &'static str {
|
module_impl!("sysinfo");
|
||||||
"sysinfo"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn spawn_controller(
|
fn spawn_controller(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
@ -6,7 +6,7 @@ use crate::clients::tray;
|
||||||
use crate::config::CommonConfig;
|
use crate::config::CommonConfig;
|
||||||
use crate::modules::tray::diff::get_diffs;
|
use crate::modules::tray::diff::get_diffs;
|
||||||
use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext};
|
use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext};
|
||||||
use crate::{glib_recv, lock, send_async, spawn};
|
use crate::{glib_recv, lock, module_impl, send_async, spawn};
|
||||||
use color_eyre::{Report, Result};
|
use color_eyre::{Report, Result};
|
||||||
use gtk::{prelude::*, PackDirection};
|
use gtk::{prelude::*, PackDirection};
|
||||||
use gtk::{IconTheme, MenuBar};
|
use gtk::{IconTheme, MenuBar};
|
||||||
|
@ -57,9 +57,7 @@ impl Module<MenuBar> for TrayModule {
|
||||||
type SendMessage = Event;
|
type SendMessage = Event;
|
||||||
type ReceiveMessage = ActivateRequest;
|
type ReceiveMessage = ActivateRequest;
|
||||||
|
|
||||||
fn name() -> &'static str {
|
module_impl!("tray");
|
||||||
"tray"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn spawn_controller(
|
fn spawn_controller(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
@ -15,7 +15,7 @@ use crate::modules::PopupButton;
|
||||||
use crate::modules::{
|
use crate::modules::{
|
||||||
Module, ModuleInfo, ModuleParts, ModulePopup, ModuleUpdateEvent, WidgetContext,
|
Module, ModuleInfo, ModuleParts, ModulePopup, ModuleUpdateEvent, WidgetContext,
|
||||||
};
|
};
|
||||||
use crate::{glib_recv, send_async, spawn, try_send};
|
use crate::{glib_recv, module_impl, send_async, spawn, try_send};
|
||||||
|
|
||||||
const DAY: i64 = 24 * 60 * 60;
|
const DAY: i64 = 24 * 60 * 60;
|
||||||
const HOUR: i64 = 60 * 60;
|
const HOUR: i64 = 60 * 60;
|
||||||
|
@ -54,9 +54,7 @@ impl Module<gtk::Button> for UpowerModule {
|
||||||
type SendMessage = UpowerProperties;
|
type SendMessage = UpowerProperties;
|
||||||
type ReceiveMessage = ();
|
type ReceiveMessage = ();
|
||||||
|
|
||||||
fn name() -> &'static str {
|
module_impl!("upower");
|
||||||
"upower"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn spawn_controller(
|
fn spawn_controller(
|
||||||
&self,
|
&self,
|
||||||
|
@ -211,7 +209,7 @@ impl Module<gtk::Button> for UpowerModule {
|
||||||
|
|
||||||
let rx = context.subscribe();
|
let rx = context.subscribe();
|
||||||
let popup = self
|
let popup = self
|
||||||
.into_popup(context.controller_tx, rx, info)
|
.into_popup(context.controller_tx.clone(), rx, context, info)
|
||||||
.into_popup_parts(vec![&button]);
|
.into_popup_parts(vec![&button]);
|
||||||
|
|
||||||
Ok(ModuleParts::new(button, popup))
|
Ok(ModuleParts::new(button, popup))
|
||||||
|
@ -221,6 +219,7 @@ impl Module<gtk::Button> for UpowerModule {
|
||||||
self,
|
self,
|
||||||
_tx: mpsc::Sender<Self::ReceiveMessage>,
|
_tx: mpsc::Sender<Self::ReceiveMessage>,
|
||||||
rx: broadcast::Receiver<Self::SendMessage>,
|
rx: broadcast::Receiver<Self::SendMessage>,
|
||||||
|
_context: WidgetContext<Self::SendMessage, Self::ReceiveMessage>,
|
||||||
_info: &ModuleInfo,
|
_info: &ModuleInfo,
|
||||||
) -> Option<gtk::Box>
|
) -> Option<gtk::Box>
|
||||||
where
|
where
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::gtk_helpers::IronbarGtkExt;
|
||||||
use crate::modules::{
|
use crate::modules::{
|
||||||
Module, ModuleInfo, ModuleParts, ModulePopup, ModuleUpdateEvent, PopupButton, WidgetContext,
|
Module, ModuleInfo, ModuleParts, ModulePopup, ModuleUpdateEvent, PopupButton, WidgetContext,
|
||||||
};
|
};
|
||||||
use crate::{glib_recv, lock, send_async, spawn, try_send};
|
use crate::{glib_recv, lock, module_impl, send_async, spawn, try_send};
|
||||||
use glib::Propagation;
|
use glib::Propagation;
|
||||||
use gtk::pango::EllipsizeMode;
|
use gtk::pango::EllipsizeMode;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
|
@ -99,9 +99,7 @@ impl Module<Button> for VolumeModule {
|
||||||
type SendMessage = Event;
|
type SendMessage = Event;
|
||||||
type ReceiveMessage = Update;
|
type ReceiveMessage = Update;
|
||||||
|
|
||||||
fn name() -> &'static str {
|
module_impl!("volume");
|
||||||
"volume"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn spawn_controller(
|
fn spawn_controller(
|
||||||
&self,
|
&self,
|
||||||
|
@ -208,7 +206,12 @@ impl Module<Button> for VolumeModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
let popup = self
|
let popup = self
|
||||||
.into_popup(context.controller_tx.clone(), context.subscribe(), info)
|
.into_popup(
|
||||||
|
context.controller_tx.clone(),
|
||||||
|
context.subscribe(),
|
||||||
|
context,
|
||||||
|
info,
|
||||||
|
)
|
||||||
.into_popup_parts(vec![&button]);
|
.into_popup_parts(vec![&button]);
|
||||||
|
|
||||||
Ok(ModuleParts::new(button, popup))
|
Ok(ModuleParts::new(button, popup))
|
||||||
|
@ -218,6 +221,7 @@ impl Module<Button> for VolumeModule {
|
||||||
self,
|
self,
|
||||||
tx: mpsc::Sender<Self::ReceiveMessage>,
|
tx: mpsc::Sender<Self::ReceiveMessage>,
|
||||||
rx: tokio::sync::broadcast::Receiver<Self::SendMessage>,
|
rx: tokio::sync::broadcast::Receiver<Self::SendMessage>,
|
||||||
|
_context: WidgetContext<Self::SendMessage, Self::ReceiveMessage>,
|
||||||
_info: &ModuleInfo,
|
_info: &ModuleInfo,
|
||||||
) -> Option<gtk::Box>
|
) -> Option<gtk::Box>
|
||||||
where
|
where
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::clients::compositor::{Visibility, Workspace, WorkspaceClient, Workspa
|
||||||
use crate::config::CommonConfig;
|
use crate::config::CommonConfig;
|
||||||
use crate::image::new_icon_button;
|
use crate::image::new_icon_button;
|
||||||
use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext};
|
use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext};
|
||||||
use crate::{glib_recv, send_async, spawn, try_send};
|
use crate::{glib_recv, module_impl, send_async, spawn, try_send};
|
||||||
use color_eyre::{Report, Result};
|
use color_eyre::{Report, Result};
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
use gtk::{Button, IconTheme};
|
use gtk::{Button, IconTheme};
|
||||||
|
@ -144,9 +144,7 @@ impl Module<gtk::Box> for WorkspacesModule {
|
||||||
type SendMessage = WorkspaceUpdate;
|
type SendMessage = WorkspaceUpdate;
|
||||||
type ReceiveMessage = String;
|
type ReceiveMessage = String;
|
||||||
|
|
||||||
fn name() -> &'static str {
|
module_impl!("workspaces");
|
||||||
"workspaces"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn spawn_controller(
|
fn spawn_controller(
|
||||||
&self,
|
&self,
|
||||||
|
|
40
src/popup.rs
40
src/popup.rs
|
@ -12,7 +12,7 @@ use tracing::{debug, trace};
|
||||||
use crate::config::BarPosition;
|
use crate::config::BarPosition;
|
||||||
use crate::gtk_helpers::{IronbarGtkExt, WidgetGeometry};
|
use crate::gtk_helpers::{IronbarGtkExt, WidgetGeometry};
|
||||||
use crate::modules::{ModuleInfo, ModulePopupParts, PopupButton};
|
use crate::modules::{ModuleInfo, ModulePopupParts, PopupButton};
|
||||||
use crate::Ironbar;
|
use crate::rc_mut;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct PopupCacheValue {
|
pub struct PopupCacheValue {
|
||||||
|
@ -23,7 +23,8 @@ pub struct PopupCacheValue {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Popup {
|
pub struct Popup {
|
||||||
pub window: ApplicationWindow,
|
pub window: ApplicationWindow,
|
||||||
pub cache: Rc<RefCell<HashMap<usize, PopupCacheValue>>>,
|
pub container_cache: Rc<RefCell<HashMap<usize, PopupCacheValue>>>,
|
||||||
|
pub button_cache: Rc<RefCell<Vec<Button>>>,
|
||||||
monitor: Monitor,
|
monitor: Monitor,
|
||||||
pos: BarPosition,
|
pos: BarPosition,
|
||||||
current_widget: Rc<RefCell<Option<(usize, usize)>>>,
|
current_widget: Rc<RefCell<Option<(usize, usize)>>>,
|
||||||
|
@ -106,10 +107,11 @@ impl Popup {
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
window: win,
|
window: win,
|
||||||
cache: Rc::new(RefCell::new(HashMap::new())),
|
container_cache: rc_mut!(HashMap::new()),
|
||||||
|
button_cache: rc_mut!(vec![]),
|
||||||
monitor: module_info.monitor.clone(),
|
monitor: module_info.monitor.clone(),
|
||||||
pos,
|
pos,
|
||||||
current_widget: Rc::new(RefCell::new(None)),
|
current_widget: rc_mut!(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,8 +119,7 @@ impl Popup {
|
||||||
debug!("Registered popup content for #{}", key);
|
debug!("Registered popup content for #{}", key);
|
||||||
|
|
||||||
for button in &content.buttons {
|
for button in &content.buttons {
|
||||||
let id = Ironbar::unique_id();
|
button.ensure_popup_id();
|
||||||
button.set_tag("popup-id", id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let orientation = self.pos.orientation();
|
let orientation = self.pos.orientation();
|
||||||
|
@ -126,7 +127,8 @@ impl Popup {
|
||||||
let window = self.window.clone();
|
let window = self.window.clone();
|
||||||
|
|
||||||
let current_widget = self.current_widget.clone();
|
let current_widget = self.current_widget.clone();
|
||||||
let cache = self.cache.clone();
|
let cache = self.container_cache.clone();
|
||||||
|
let button_cache = self.button_cache.clone();
|
||||||
|
|
||||||
content
|
content
|
||||||
.container
|
.container
|
||||||
|
@ -135,11 +137,9 @@ impl Popup {
|
||||||
trace!("Resized: {}x{}", rect.width(), rect.height());
|
trace!("Resized: {}x{}", rect.width(), rect.height());
|
||||||
|
|
||||||
if let Some((widget_id, button_id)) = *current_widget.borrow() {
|
if let Some((widget_id, button_id)) = *current_widget.borrow() {
|
||||||
if let Some(PopupCacheValue { content, .. }) =
|
if let Some(PopupCacheValue { .. }) = cache.borrow().get(&widget_id) {
|
||||||
cache.borrow().get(&widget_id)
|
|
||||||
{
|
|
||||||
Self::set_position(
|
Self::set_position(
|
||||||
&content.buttons,
|
&button_cache.borrow(),
|
||||||
button_id,
|
button_id,
|
||||||
orientation,
|
orientation,
|
||||||
&monitor,
|
&monitor,
|
||||||
|
@ -150,7 +150,11 @@ impl Popup {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
self.cache
|
self.button_cache
|
||||||
|
.borrow_mut()
|
||||||
|
.append(&mut content.buttons.clone());
|
||||||
|
|
||||||
|
self.container_cache
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.insert(key, PopupCacheValue { name, content });
|
.insert(key, PopupCacheValue { name, content });
|
||||||
}
|
}
|
||||||
|
@ -158,16 +162,17 @@ impl Popup {
|
||||||
pub fn show(&self, widget_id: usize, button_id: usize) {
|
pub fn show(&self, widget_id: usize, button_id: usize) {
|
||||||
self.clear_window();
|
self.clear_window();
|
||||||
|
|
||||||
if let Some(PopupCacheValue { content, .. }) = self.cache.borrow().get(&widget_id) {
|
if let Some(PopupCacheValue { content, .. }) = self.container_cache.borrow().get(&widget_id)
|
||||||
|
{
|
||||||
*self.current_widget.borrow_mut() = Some((widget_id, button_id));
|
*self.current_widget.borrow_mut() = Some((widget_id, button_id));
|
||||||
|
|
||||||
content.container.style_context().add_class("popup");
|
content.container.add_class("popup");
|
||||||
self.window.add(&content.container);
|
self.window.add(&content.container);
|
||||||
|
|
||||||
self.window.show();
|
self.window.show();
|
||||||
|
|
||||||
Self::set_position(
|
Self::set_position(
|
||||||
&content.buttons,
|
&self.button_cache.borrow(),
|
||||||
button_id,
|
button_id,
|
||||||
self.pos.orientation(),
|
self.pos.orientation(),
|
||||||
&self.monitor,
|
&self.monitor,
|
||||||
|
@ -179,8 +184,9 @@ impl Popup {
|
||||||
pub fn show_at(&self, widget_id: usize, geometry: WidgetGeometry) {
|
pub fn show_at(&self, widget_id: usize, geometry: WidgetGeometry) {
|
||||||
self.clear_window();
|
self.clear_window();
|
||||||
|
|
||||||
if let Some(PopupCacheValue { content, .. }) = self.cache.borrow().get(&widget_id) {
|
if let Some(PopupCacheValue { content, .. }) = self.container_cache.borrow().get(&widget_id)
|
||||||
content.container.style_context().add_class("popup");
|
{
|
||||||
|
content.container.add_class("popup");
|
||||||
self.window.add(&content.container);
|
self.window.add(&content.container);
|
||||||
|
|
||||||
self.window.show();
|
self.window.show();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue