1
0
Fork 0
mirror of https://github.com/Zedfrigg/ironbar.git synced 2025-07-01 18:51:04 +02:00

Merge pull request #459 from JakeStanger/fix/popup-resize

fix(popup): re-position on resize due to content change
This commit is contained in:
Jake Stanger 2024-02-18 15:36:52 +00:00 committed by GitHub
commit 180a5205b9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 114 additions and 81 deletions

View file

@ -10,7 +10,6 @@ use gtk::gdk::Monitor;
use gtk::prelude::*; use gtk::prelude::*;
use gtk::{Application, ApplicationWindow, IconTheme, Orientation, Window, WindowType}; use gtk::{Application, ApplicationWindow, IconTheme, Orientation, Window, WindowType};
use gtk_layer_shell::LayerShell; use gtk_layer_shell::LayerShell;
use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
use std::time::Duration; use std::time::Duration;
use tracing::{debug, info}; use tracing::{debug, info};
@ -18,7 +17,7 @@ use tracing::{debug, info};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
enum Inner { enum Inner {
New { config: Option<Config> }, New { config: Option<Config> },
Loaded { popup: Rc<RefCell<Popup>> }, Loaded { popup: Rc<Popup> },
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -60,7 +59,7 @@ impl Bar {
window.set_widget_name(&name); window.set_widget_name(&name);
let position = config.position; let position = config.position;
let orientation = position.get_orientation(); let orientation = position.orientation();
let content = gtk::Box::builder() let content = gtk::Box::builder()
.orientation(orientation) .orientation(orientation)
@ -187,7 +186,7 @@ impl Bar {
win.set_layer_shell_margin(gtk_layer_shell::Edge::Left, margin.left); win.set_layer_shell_margin(gtk_layer_shell::Edge::Left, margin.left);
win.set_layer_shell_margin(gtk_layer_shell::Edge::Right, margin.right); win.set_layer_shell_margin(gtk_layer_shell::Edge::Right, margin.right);
let bar_orientation = position.get_orientation(); let bar_orientation = position.orientation();
win.set_anchor( win.set_anchor(
gtk_layer_shell::Edge::Top, gtk_layer_shell::Edge::Top,
@ -269,7 +268,7 @@ impl Bar {
// popup ignores module location so can bodge this for now // popup ignores module location so can bodge this for now
let popup = Popup::new(&info!(ModuleLocation::Left), config.popup_gap); let popup = Popup::new(&info!(ModuleLocation::Left), config.popup_gap);
let popup = Rc::new(RefCell::new(popup)); let popup = Rc::new(popup);
if let Some(modules) = config.start { if let Some(modules) = config.start {
let info = info!(ModuleLocation::Left); let info = info!(ModuleLocation::Left);
@ -315,7 +314,7 @@ impl Bar {
&self.monitor_name &self.monitor_name
} }
pub fn popup(&self) -> Rc<RefCell<Popup>> { pub fn popup(&self) -> Rc<Popup> {
match &self.inner { match &self.inner {
Inner::New { .. } => { Inner::New { .. } => {
panic!("Attempted to get popup of uninitialized bar. This is a serious bug!") panic!("Attempted to get popup of uninitialized bar. This is a serious bug!")
@ -339,7 +338,7 @@ fn create_container(name: &str, orientation: Orientation) -> gtk::Box {
#[derive(Debug)] #[derive(Debug)]
struct BarLoadResult { struct BarLoadResult {
popup: Rc<RefCell<Popup>>, popup: Rc<Popup>,
} }
/// Adds modules into a provided GTK box, /// Adds modules into a provided GTK box,
@ -349,9 +348,9 @@ fn add_modules(
modules: Vec<ModuleConfig>, modules: Vec<ModuleConfig>,
info: &ModuleInfo, info: &ModuleInfo,
ironbar: &Rc<Ironbar>, ironbar: &Rc<Ironbar>,
popup: &Rc<RefCell<Popup>>, popup: &Rc<Popup>,
) -> Result<()> { ) -> Result<()> {
let orientation = info.bar_position.get_orientation(); let orientation = info.bar_position.orientation();
macro_rules! add_module { macro_rules! add_module {
($module:expr, $id:expr) => {{ ($module:expr, $id:expr) => {{

View file

@ -38,7 +38,7 @@ impl<'de> Deserialize<'de> for MonitorConfig {
impl BarPosition { impl BarPosition {
/// Gets the orientation the bar and widgets should use /// Gets the orientation the bar and widgets should use
/// based on this position. /// based on this position.
pub fn get_orientation(self) -> Orientation { pub fn orientation(self) -> Orientation {
if self == Self::Top || self == Self::Bottom { if self == Self::Top || self == Self::Bottom {
Orientation::Horizontal Orientation::Horizontal
} else { } else {

View file

@ -167,13 +167,13 @@ impl Ipc {
match bar { match bar {
Some(bar) => { Some(bar) => {
let popup = bar.popup(); let popup = bar.popup();
let current_widget = popup.borrow().current_widget(); let current_widget = popup.current_widget();
popup.borrow_mut().hide(); popup.hide();
let data = popup let data = popup
.borrow()
.cache .cache
.borrow()
.iter() .iter()
.find(|(_, value)| value.name == name) .find(|(_, value)| value.name == name)
.map(|(id, value)| (*id, value.content.buttons.first().cloned())); .map(|(id, value)| (*id, value.content.buttons.first().cloned()));
@ -181,7 +181,6 @@ impl Ipc {
match data { match data {
Some((id, Some(button))) if current_widget != Some(id) => { Some((id, Some(button))) if current_widget != Some(id) => {
let button_id = button.popup_id(); let button_id = button.popup_id();
let mut popup = popup.borrow_mut();
if popup.is_visible() { if popup.is_visible() {
popup.hide(); popup.hide();
@ -207,11 +206,11 @@ impl Ipc {
let popup = bar.popup(); let popup = bar.popup();
// only one popup per bar, so hide if open for another widget // only one popup per bar, so hide if open for another widget
popup.borrow_mut().hide(); popup.hide();
let data = popup let data = popup
.borrow()
.cache .cache
.borrow()
.iter() .iter()
.find(|(_, value)| value.name == name) .find(|(_, value)| value.name == name)
.map(|(id, value)| (*id, value.content.buttons.first().cloned())); .map(|(id, value)| (*id, value.content.buttons.first().cloned()));
@ -219,7 +218,7 @@ impl Ipc {
match data { match data {
Some((id, Some(button))) => { Some((id, Some(button))) => {
let button_id = button.popup_id(); let button_id = button.popup_id();
popup.borrow_mut().show(id, button_id); popup.show(id, button_id);
Response::Ok Response::Ok
} }
@ -236,7 +235,7 @@ impl Ipc {
match bar { match bar {
Some(bar) => { Some(bar) => {
let popup = bar.popup(); let popup = bar.popup();
popup.borrow_mut().hide(); popup.hide();
Response::Ok Response::Ok
} }

View file

@ -194,7 +194,7 @@ impl Module<gtk::Box> for CustomModule {
context: WidgetContext<Self::SendMessage, Self::ReceiveMessage>, context: WidgetContext<Self::SendMessage, Self::ReceiveMessage>,
info: &ModuleInfo, info: &ModuleInfo,
) -> Result<ModuleParts<gtk::Box>> { ) -> Result<ModuleParts<gtk::Box>> {
let orientation = info.bar_position.get_orientation(); let orientation = info.bar_position.orientation();
let container = gtk::Box::builder().orientation(orientation).build(); let container = gtk::Box::builder().orientation(orientation).build();
let popup_buttons = Rc::new(RefCell::new(Vec::new())); let popup_buttons = Rc::new(RefCell::new(Vec::new()));
@ -236,7 +236,7 @@ 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 {
tx: &tx, tx: &tx,
bar_orientation: info.bar_position.get_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![])),
}; };

View file

@ -113,7 +113,7 @@ impl Module<gtk::Box> for FocusedModule {
) -> Result<ModuleParts<gtk::Box>> { ) -> Result<ModuleParts<gtk::Box>> {
let icon_theme = info.icon_theme; let icon_theme = info.icon_theme;
let container = gtk::Box::new(info.bar_position.get_orientation(), 5); let container = gtk::Box::new(info.bar_position.orientation(), 5);
let icon = gtk::Image::new(); let icon = gtk::Image::new();
if self.show_icon { if self.show_icon {

View file

@ -225,9 +225,7 @@ impl ItemButton {
try_send!( try_send!(
tx, tx,
ModuleUpdateEvent::OpenPopupAt( ModuleUpdateEvent::OpenPopupAt(button.geometry(bar_position.orientation()))
button.geometry(bar_position.get_orientation())
)
); );
} else { } else {
try_send!(tx, ModuleUpdateEvent::ClosePopup); try_send!(tx, ModuleUpdateEvent::ClosePopup);

View file

@ -301,7 +301,7 @@ impl Module<gtk::Box> for LauncherModule {
) -> crate::Result<ModuleParts<gtk::Box>> { ) -> crate::Result<ModuleParts<gtk::Box>> {
let icon_theme = info.icon_theme; let icon_theme = info.icon_theme;
let container = gtk::Box::new(info.bar_position.get_orientation(), 0); let container = gtk::Box::new(info.bar_position.orientation(), 0);
{ {
let container = container.clone(); let container = container.clone();

View file

@ -1,4 +1,3 @@
use std::cell::RefCell;
use std::fmt::Debug; use std::fmt::Debug;
use std::rc::Rc; use std::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
@ -215,7 +214,7 @@ pub fn create_module<TModule, TWidget, TSend, TRec>(
ironbar: Rc<Ironbar>, ironbar: Rc<Ironbar>,
name: Option<String>, name: Option<String>,
info: &ModuleInfo, info: &ModuleInfo,
popup: &Rc<RefCell<Popup>>, popup: &Rc<Popup>,
) -> Result<ModuleParts<TWidget>> ) -> Result<ModuleParts<TWidget>>
where where
TModule: Module<TWidget, SendMessage = TSend, ReceiveMessage = TRec>, TModule: Module<TWidget, SendMessage = TSend, ReceiveMessage = TRec>,
@ -251,7 +250,7 @@ where
.style_context() .style_context()
.add_class(&format!("popup-{module_name}")); .add_class(&format!("popup-{module_name}"));
register_popup_content(popup, id, instance_name, popup_content); popup.register_content(id, instance_name, popup_content);
} }
setup_receiver(tx, ui_rx, popup.clone(), module_name, id); setup_receiver(tx, ui_rx, popup.clone(), module_name, id);
@ -259,16 +258,6 @@ where
Ok(module_parts) Ok(module_parts)
} }
/// Registers the popup content with the popup.
fn register_popup_content(
popup: &Rc<RefCell<Popup>>,
id: usize,
name: String,
popup_content: ModulePopupParts,
) {
popup.borrow_mut().register_content(id, name, popup_content);
}
/// Sets up the bridge channel receiver /// Sets up the bridge channel receiver
/// to pick up events from the controller, widget or popup. /// to pick up events from the controller, widget or popup.
/// ///
@ -277,7 +266,7 @@ fn register_popup_content(
fn setup_receiver<TSend>( fn setup_receiver<TSend>(
tx: broadcast::Sender<TSend>, tx: broadcast::Sender<TSend>,
rx: mpsc::Receiver<ModuleUpdateEvent<TSend>>, rx: mpsc::Receiver<ModuleUpdateEvent<TSend>>,
popup: Rc<RefCell<Popup>>, popup: Rc<Popup>,
name: &'static str, name: &'static str,
id: usize, id: usize,
) where ) where
@ -294,7 +283,6 @@ fn setup_receiver<TSend>(
} }
ModuleUpdateEvent::TogglePopup(button_id) => { ModuleUpdateEvent::TogglePopup(button_id) => {
debug!("Toggling popup for {} [#{}]", name, id); debug!("Toggling popup for {} [#{}]", name, id);
let mut popup = popup.borrow_mut();
if popup.is_visible() { if popup.is_visible() {
popup.hide(); popup.hide();
} else { } else {
@ -309,8 +297,6 @@ fn setup_receiver<TSend>(
} }
ModuleUpdateEvent::OpenPopup(button_id) => { ModuleUpdateEvent::OpenPopup(button_id) => {
debug!("Opening popup for {} [#{}]", name, id); debug!("Opening popup for {} [#{}]", name, id);
let mut popup = popup.borrow_mut();
popup.hide(); popup.hide();
popup.show(id, button_id); popup.show(id, button_id);
@ -324,7 +310,6 @@ fn setup_receiver<TSend>(
ModuleUpdateEvent::OpenPopupAt(geometry) => { ModuleUpdateEvent::OpenPopupAt(geometry) => {
debug!("Opening popup for {} [#{}]", name, id); debug!("Opening popup for {} [#{}]", name, id);
let mut popup = popup.borrow_mut();
popup.hide(); popup.hide();
popup.show_at(id, geometry); popup.show_at(id, geometry);
@ -336,8 +321,6 @@ fn setup_receiver<TSend>(
} }
ModuleUpdateEvent::ClosePopup => { ModuleUpdateEvent::ClosePopup => {
debug!("Closing popup for {} [#{}]", name, id); debug!("Closing popup for {} [#{}]", name, id);
let mut popup = popup.borrow_mut();
popup.hide(); popup.hide();
} }
} }

View file

@ -188,7 +188,7 @@ impl Module<gtk::Box> for SysInfoModule {
) -> Result<ModuleParts<gtk::Box>> { ) -> Result<ModuleParts<gtk::Box>> {
let re = Regex::new(r"\{([^}]+)}")?; let re = Regex::new(r"\{([^}]+)}")?;
let container = gtk::Box::new(info.bar_position.get_orientation(), 10); let container = gtk::Box::new(info.bar_position.orientation(), 10);
let mut labels = Vec::new(); let mut labels = Vec::new();

View file

@ -89,7 +89,7 @@ impl Module<MenuBar> for TrayModule {
let container = MenuBar::new(); let container = MenuBar::new();
let direction = self.direction.unwrap_or( let direction = self.direction.unwrap_or(
if info.bar_position.get_orientation() == gtk::Orientation::Vertical { if info.bar_position.orientation() == gtk::Orientation::Vertical {
PackDirection::Ttb PackDirection::Ttb
} else { } else {
PackDirection::Ltr PackDirection::Ltr

View file

@ -189,7 +189,7 @@ impl Module<gtk::Box> for WorkspacesModule {
context: WidgetContext<Self::SendMessage, Self::ReceiveMessage>, context: WidgetContext<Self::SendMessage, Self::ReceiveMessage>,
info: &ModuleInfo, info: &ModuleInfo,
) -> Result<ModuleParts<gtk::Box>> { ) -> Result<ModuleParts<gtk::Box>> {
let container = gtk::Box::new(info.bar_position.get_orientation(), 0); let container = gtk::Box::new(info.bar_position.orientation(), 0);
let name_map = self.name_map.clone().unwrap_or_default(); let name_map = self.name_map.clone().unwrap_or_default();
let favs = self.favorites.clone(); let favs = self.favorites.clone();

View file

@ -1,11 +1,13 @@
use glib::Propagation; use glib::Propagation;
use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::rc::Rc;
use gtk::gdk::Monitor; use gtk::gdk::Monitor;
use gtk::prelude::*; use gtk::prelude::*;
use gtk::{ApplicationWindow, Orientation}; use gtk::{ApplicationWindow, Button, Orientation};
use gtk_layer_shell::LayerShell; use gtk_layer_shell::LayerShell;
use tracing::debug; use tracing::{debug, trace};
use crate::config::BarPosition; use crate::config::BarPosition;
use crate::gtk_helpers::{IronbarGtkExt, WidgetGeometry}; use crate::gtk_helpers::{IronbarGtkExt, WidgetGeometry};
@ -21,10 +23,10 @@ pub struct PopupCacheValue {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Popup { pub struct Popup {
pub window: ApplicationWindow, pub window: ApplicationWindow,
pub cache: HashMap<usize, PopupCacheValue>, pub cache: Rc<RefCell<HashMap<usize, PopupCacheValue>>>,
monitor: Monitor, monitor: Monitor,
pos: BarPosition, pos: BarPosition,
current_widget: Option<usize>, current_widget: Rc<RefCell<Option<(usize, usize)>>>,
} }
impl Popup { impl Popup {
@ -33,7 +35,7 @@ impl Popup {
/// and an empty `gtk::Box` container. /// and an empty `gtk::Box` container.
pub fn new(module_info: &ModuleInfo, gap: i32) -> Self { pub fn new(module_info: &ModuleInfo, gap: i32) -> Self {
let pos = module_info.bar_position; let pos = module_info.bar_position;
let orientation = pos.get_orientation(); let orientation = pos.orientation();
let win = ApplicationWindow::builder() let win = ApplicationWindow::builder()
.application(module_info.app) .application(module_info.app)
@ -104,14 +106,14 @@ impl Popup {
Self { Self {
window: win, window: win,
cache: HashMap::new(), cache: Rc::new(RefCell::new(HashMap::new())),
monitor: module_info.monitor.clone(), monitor: module_info.monitor.clone(),
pos, pos,
current_widget: None, current_widget: Rc::new(RefCell::new(None)),
} }
} }
pub fn register_content(&mut self, key: usize, name: String, content: ModulePopupParts) { pub fn register_content(&self, key: usize, name: String, content: ModulePopupParts) {
debug!("Registered popup content for #{}", key); debug!("Registered popup content for #{}", key);
for button in &content.buttons { for button in &content.buttons {
@ -119,45 +121,94 @@ impl Popup {
button.set_tag("popup-id", id); button.set_tag("popup-id", id);
} }
self.cache.insert(key, PopupCacheValue { name, content }); let orientation = self.pos.orientation();
let monitor = self.monitor.clone();
let window = self.window.clone();
let current_widget = self.current_widget.clone();
let cache = self.cache.clone();
content
.container
.connect_size_allocate(move |container, rect| {
if container.is_visible() {
trace!("Resized: {}x{}", rect.width(), rect.height());
if let Some((widget_id, button_id)) = *current_widget.borrow() {
if let Some(PopupCacheValue { content, .. }) =
cache.borrow().get(&widget_id)
{
Self::set_position(
&content.buttons,
button_id,
orientation,
&monitor,
&window,
);
}
}
}
});
self.cache
.borrow_mut()
.insert(key, PopupCacheValue { name, content });
} }
pub fn show(&mut 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.get(&widget_id) { if let Some(PopupCacheValue { content, .. }) = self.cache.borrow().get(&widget_id) {
self.current_widget = Some(widget_id); *self.current_widget.borrow_mut() = Some((widget_id, button_id));
content.container.style_context().add_class("popup"); content.container.style_context().add_class("popup");
self.window.add(&content.container); self.window.add(&content.container);
self.window.show(); self.window.show();
let button = content Self::set_position(
.buttons &content.buttons,
.iter() button_id,
.find(|b| b.popup_id() == button_id) self.pos.orientation(),
.expect("to find valid button"); &self.monitor,
&self.window,
let orientation = self.pos.get_orientation(); );
let geometry = button.geometry(orientation);
self.set_pos(geometry);
} }
} }
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.get(&widget_id) { if let Some(PopupCacheValue { content, .. }) = self.cache.borrow().get(&widget_id) {
content.container.style_context().add_class("popup"); content.container.style_context().add_class("popup");
self.window.add(&content.container); self.window.add(&content.container);
self.window.show(); self.window.show();
self.set_pos(geometry); Self::set_pos(
geometry,
self.pos.orientation(),
&self.monitor,
&self.window,
);
} }
} }
fn set_position(
buttons: &[Button],
button_id: usize,
orientation: Orientation,
monitor: &Monitor,
window: &ApplicationWindow,
) {
let button = buttons
.iter()
.find(|b| b.popup_id() == button_id)
.expect("to find valid button");
let geometry = button.geometry(orientation);
Self::set_pos(geometry, orientation, monitor, window);
}
fn clear_window(&self) { fn clear_window(&self) {
let children = self.window.children(); let children = self.window.children();
for child in children { for child in children {
@ -166,8 +217,8 @@ impl Popup {
} }
/// Hides the popover /// Hides the popover
pub fn hide(&mut self) { pub fn hide(&self) {
self.current_widget = None; *self.current_widget.borrow_mut() = None;
self.window.hide(); self.window.hide();
} }
@ -177,22 +228,25 @@ impl Popup {
} }
pub fn current_widget(&self) -> Option<usize> { pub fn current_widget(&self) -> Option<usize> {
self.current_widget self.current_widget.borrow().map(|w| w.0)
} }
/// Sets the popup's X/Y position relative to the left or border of the screen /// Sets the popup's X/Y position relative to the left or border of the screen
/// (depending on orientation). /// (depending on orientation).
fn set_pos(&self, geometry: WidgetGeometry) { fn set_pos(
let orientation = self.pos.get_orientation(); geometry: WidgetGeometry,
orientation: Orientation,
let mon_workarea = self.monitor.workarea(); monitor: &Monitor,
window: &ApplicationWindow,
) {
let mon_workarea = monitor.workarea();
let screen_size = if orientation == Orientation::Horizontal { let screen_size = if orientation == Orientation::Horizontal {
mon_workarea.width() mon_workarea.width()
} else { } else {
mon_workarea.height() mon_workarea.height()
}; };
let (popup_width, popup_height) = self.window.size(); let (popup_width, popup_height) = window.size();
let popup_size = if orientation == Orientation::Horizontal { let popup_size = if orientation == Orientation::Horizontal {
popup_width popup_width
} else { } else {
@ -217,6 +271,6 @@ impl Popup {
gtk_layer_shell::Edge::Top gtk_layer_shell::Edge::Top
}; };
self.window.set_layer_shell_margin(edge, offset as i32); window.set_layer_shell_margin(edge, offset as i32);
} }
} }