oxwm

https://git.tonybtw.com/oxwm.git git://git.tonybtw.com/oxwm.git

Added bar. 2mb!

Commit
8347143b8d3a88e7022f0fe58ce180eee03238af
Parent
1a0575c
Author
tonybtw <tonybtw@tonybtw.com>
Date
2025-10-03 06:39:35

Diff

diff --git a/readme.org b/readme.org
index cad6ea9..90f0d7f 100644
--- a/readme.org
+++ b/readme.org
@@ -6,9 +6,6 @@ A dynamic window manager written in Rust, inspired by dwm but designed to evolve
 on its own. Configuration is done in Rust source code, keeping with the suckless
 philosophy of *"edit + recompile."*
 
-This project is still in its early stages. Currently, it can claim ownership of
-an X display and log incoming events.
-
 * Project Structure
 
 #+begin_src sh
@@ -22,34 +19,58 @@ src/
 │   │   ├── connection: RustConnection   [X11 connection]
 │   │   ├── windows: Vec<Window>         [All managed windows]
 │   │   ├── focused_window: Option<Window>
-│   │   └── layout: Box<dyn Layout>
+│   │   ├── layout: Box<dyn Layout>
+│   │   ├── window_tags: HashMap<Window, TagMask>
+│   │   ├── selected_tags: TagMask
+│   │   └── bar: Bar                     [Status bar]
 │   │
-│   ├── new()                            [Initialize WM, grab root]
+│   ├── new()                            [Initialize WM, grab root, create bar]
 │   ├── run()                            [Main event loop]
 │   ├── handle_event()                   [Route X11 events]
-│   │   ├── MapRequest    → add window, apply layout
-│   │   ├── UnmapNotify   → remove window
-│   │   ├── DestroyNotify → remove window
-│   │   └── KeyPress      → get action, handle it
+│   │   ├── MapRequest    → add window, apply layout, update bar
+│   │   ├── UnmapNotify   → remove window, update bar
+│   │   ├── DestroyNotify → remove window, update bar
+│   │   ├── KeyPress      → get action, handle it
+│   │   ├── ButtonPress   → handle bar clicks
+│   │   └── Expose        → redraw bar
 │   ├── handle_key_action()              [Execute keyboard actions]
 │   ├── remove_window()                  [Remove from Vec, reapply layout]
 │   ├── set_focus()                      [Focus window, update visuals]
-│   ├── cycle_focus()                    [Move focus to next window]
+│   ├── cycle_focus()                    [Move focus to next/prev window]
+│   ├── view_tag()                       [Switch to tag/workspace]
+│   ├── move_to_tag()                    [Move window to tag]
+│   ├── update_bar()                     [Calculate occupied tags, redraw bar]
 │   ├── update_focus_visuals()           [Set border colors]
-│   └── apply_layout()                   [Position all windows]
+│   └── apply_layout()                   [Position all windows below bar]
+│
+├── config.rs                            [CONFIGURATION - all settings here]
+│   ├── BORDER_WIDTH, BORDER_FOCUSED, BORDER_UNFOCUSED
+│   ├── TAG_COUNT, TAGS                  [Workspace configuration]
+│   ├── TERMINAL, MODKEY
+│   └── KEYBINDINGS                      [All keybinds as const array]
+│
+├── bar/
+│   ├── mod.rs                           [Re-exports, constants]
+│   └── bar.rs
+│       ├── struct Bar                   [Status bar window]
+│       ├── new()                        [Create bar X11 window]
+│       ├── draw()                       [Render tags with state indicators]
+│       ├── handle_click()               [Detect which tag was clicked]
+│       └── invalidate()                 [Mark bar as needing redraw]
 │
 ├── keyboard/
 │   ├── mod.rs                           [Re-exports]
 │   ├── keycodes.rs                      [Key constants: Q, J, RETURN, etc]
 │   └── handlers.rs
-│       ├── enum KeyAction               [SpawnTerminal, CloseWindow, CycleWindow, Quit, None]
+│       ├── enum KeyAction               [Spawn, KillClient, FocusStack, ViewTag, etc]
+│       ├── enum Arg                     [None, Int, Str, Array]
 │       ├── setup_keybinds()             [Register keys with X11]
 │       └── handle_key_press()           [Parse KeyPressEvent → KeyAction]
 │
 └── layout/
-        ├── mod.rs                       [Layout trait definition]
-        └── tiling.rs
-           └── TilingLayout::arrange()      [Calculate window positions]
+    ├── mod.rs                           [Layout trait definition]
+    └── tiling.rs
+        └── TilingLayout::arrange()      [Calculate window positions]
 #+end_src
 
 * Event Flow
@@ -59,19 +80,27 @@ src/
 3. For KeyPress:
    - keyboard::handle_key_press() → KeyAction
    - handle_key_action() executes action
+   - update_bar() if tags/windows changed
 4. For Map/Unmap:
-   - Modify windows Vec
-   - apply_layout() repositions everything
+   - Modify windows Vec and window_tags HashMap
+   - apply_layout() repositions everything (accounting for bar)
+   - update_bar() shows occupied tags
    - update_focus_visuals() redraws borders
+5. For ButtonPress on bar:
+   - bar.handle_click() determines clicked tag
+   - view_tag() switches workspace
 
 * Key Bindings
 
-| Binding       | Action               |
-|---------------+----------------------|
-| Alt+Return    | Spawn terminal       |
-| Alt+J         | Cycle focus          |
-| Alt+Shift+Q   | Close focused window |
-| Alt+Shift+Q   | Quit WM              |
+| Binding         | Action                  |
+|-----------------+-------------------------|
+| Alt+Return      | Spawn terminal          |
+| Alt+J/K         | Cycle focus down/up     |
+| Alt+Q           | Kill focused window     |
+| Alt+Shift+Q     | Quit WM                 |
+| Alt+1-9         | View tag 1-9            |
+| Alt+Shift+1-9   | Move window to tag 1-9  |
+| Alt+S           | Screenshot (maim)       |
 
 * ⚙ Installation — Running with Nix Flakes
 You can set up a reproducible development environment with Rust, Cargo, Xephyr, xterm, and
@@ -100,55 +129,97 @@ just test
 This should open a new Xephyr window. oxwm will attach to it and log X11
 events in your host terminal. Clients like xterm/xclock will appear inside Xephyr.
 
-* OXWM Todo List:
-** TODO Reorganization Tasks [1/2]
-- [X] Move keyboard module to folder structure:
-  - [X] Create =src/keyboard/mod.rs= with re-exports
-  - [X] Move constants to =src/keyboard/keycodes.rs=
-  - [X] Move key handlers to =src/keyboard/handlers.rs=
-  - [X] Update imports in main.rs and window_manager.rs
-- [ ] Create =src/config.rs= in root directory for future configuration system
-
-** TODO Core Window Management [1/2]
+* Current Status
+** Working Features
+- ✓ X11 event handling and window management
+- ✓ Tag system (9 workspaces) with keyboard switching
+- ✓ Window focus cycling (Alt+J/K)
+- ✓ Tiling layout with border indicators
+- ✓ Status bar showing tags
+  - Visual indicators: selected (white), occupied (gray line), empty (dim)
+  - Click-to-switch tags
+  - Performance-optimized redrawing
+- ✓ Basic keybindings (spawn, kill, focus, tags)
+- ✓ Configuration via Rust constants in config.rs
+
+** Immediate Next Steps
+- [ ] Status text in bar (date, time, system info)
+- [ ] dmenu integration for application launcher
+- [ ] Additional widgets (clock, battery, etc.)
+
+** Long Term Roadmap
+- [ ] Multi-monitor support
+- [ ] Additional layouts (monocle, floating, etc.)
+- [ ] Per-window floating behavior
+- [ ] Per-program rules (auto-tag assignment, floating rules)
+- [ ] Master area resizing
+- [ ] Window swapping in layout
+- [ ] Configurable gaps between windows
+- [ ] External bar support (polybar, lemonbar, etc.)
+
+* OXWM Development Todo
+** DONE Core Window Management [2/2]
 - [X] Fix layout after program is closed (handle UnmapNotify events)
   - [X] Add UnmapNotify to event handling
   - [X] Remove closed windows from windows vector
   - [X] Re-apply layout after window removal
-- [ ] Add keybind to swap focus between windows
-  - [ ] Track focused window in WindowManager struct
-  - [ ] Implement focus cycling logic
-  - [ ] Add visual focus indication (borders/colors)
-
-** Key System Improvements
-- [ ] Connect config.rs to keyboard system for dynamic keybind generation
-- [ ] Add more dwm-like keybinds:
-  - [ ] Window focus switching (Alt+J/K)
-  - [ ] Master area resizing
-  - [ ] Layout switching
-  - [ ] Workspace/tag management
-- [ ] Better error handling for failed key grabs
-
-** Layout System
-- [ ] Add more layout types (monocle, floating)
+- [X] Add keybind to swap focus between windows
+  - [X] Track focused window in WindowManager struct
+  - [X] Implement focus cycling logic
+  - [X] Add visual focus indication (borders/colors)
+
+** DONE Tag System [3/3]
+- [X] Implement tag/workspace system (9 tags)
+- [X] Keybinds to switch tags (Alt+1-9)
+- [X] Keybinds to move windows to tags (Alt+Shift+1-9)
+
+** DONE Status Bar [2/2]
+- [X] Create basic bar window at screen top
+- [X] Display tag indicators with state (selected/occupied/empty)
+
+** IN PROGRESS Bar Enhancements [0/3]
+- [ ] Add status text area (right side of bar)
+- [ ] Implement clock widget
+- [ ] Add system information widgets
+
+** TODO Key System Improvements [0/2]
+- [ ] dmenu integration for application launching
+- [ ] More spawn commands in config (screenshot, volume, etc.)
+
+** TODO Layout System [0/4]
+- [ ] Add monocle layout
+- [ ] Add floating layout mode
 - [ ] Handle window resize requests properly
 - [ ] Add configurable gaps between windows
-- [ ] Implement layout switching keybinds
+
+** TODO Advanced Features [0/3]
+- [ ] Multi-monitor support
+- [ ] Per-window rules (floating, tag assignment)
+- [ ] Master area resizing keybinds
 
 ** Polish & Features
 - [ ] Clean window destruction/cleanup
 - [ ] Handle edge cases (empty window list, invalid windows)
-- [ ] Add status bar integration
 - [ ] Better error messages and logging
-
-** Priority
-Reorganization and UnmapNotify handling should be immediate priorities.
-
-* Status
-- Rust + x11rb skeleton running
-- Nix flake devShell available
-- =just test= launches Xephyr, clients, and oxwm
+- [ ] Proper font rendering in bar (currently using basic X11 text)
+
+* Architecture Notes
+** Tag System
+Tags are implemented as bitmasks (TagMask = u32), allowing windows to belong to
+multiple tags simultaneously (though current UI only supports single tags).
+Each window has an associated TagMask in window_tags HashMap.
+
+** Bar Design
+The bar uses a performance-optimized approach:
+- Only redraws when invalidate() is called
+- Pre-calculates tag widths on creation
+- Uses X11 graphics context for efficient drawing
+- Click handling uses O(n) tag width lookup
+
+** Configuration Philosophy
+Following dwm's approach: all configuration is in Rust source code. No runtime
+config files. Edit config.rs and recompile. This ensures type safety and
+compile-time validation of all settings.
 
 * License
 [[https://www.gnu.org/licenses/gpl-3.0.en.html][GPL]]
-
-
diff --git a/src/bar/bar.rs b/src/bar/bar.rs
new file mode 100644
index 0000000..05970c2
--- /dev/null
+++ b/src/bar/bar.rs
@@ -0,0 +1,185 @@
+use super::BAR_HEIGHT;
+use crate::config::TAGS;
+use anyhow::Result;
+use x11rb::COPY_DEPTH_FROM_PARENT;
+use x11rb::connection::Connection;
+use x11rb::protocol::xproto::*;
+
+pub struct Bar {
+    window: Window,
+    width: u16,
+    height: u16,
+    graphics_context: Gcontext,
+
+    tag_widths: Vec<u16>,
+    needs_redraw: bool,
+}
+
+impl Bar {
+    pub fn new<C: Connection>(connection: &C, screen: &Screen) -> Result<Self> {
+        let window = connection.generate_id()?;
+        let graphics_context = connection.generate_id()?;
+
+        let width = screen.width_in_pixels;
+        let height = BAR_HEIGHT;
+
+        connection.create_window(
+            COPY_DEPTH_FROM_PARENT,
+            window,
+            screen.root,
+            0,
+            0,
+            width,
+            height,
+            0,
+            WindowClass::INPUT_OUTPUT,
+            screen.root_visual,
+            &CreateWindowAux::new()
+                .background_pixel(screen.black_pixel)
+                .event_mask(EventMask::EXPOSURE | EventMask::BUTTON_PRESS)
+                .override_redirect(1),
+        )?;
+
+        connection.create_gc(
+            graphics_context,
+            window,
+            &CreateGCAux::new()
+                .foreground(screen.white_pixel)
+                .background(screen.black_pixel),
+        )?;
+
+        connection.map_window(window)?;
+        connection.flush()?;
+
+        // TODO: actual text width calculation when we add fonts
+        let tag_widths = TAGS.iter().map(|tag| (tag.len() as u16 * 8) + 10).collect();
+
+        Ok(Bar {
+            window,
+            width,
+            height,
+            graphics_context,
+            tag_widths,
+            needs_redraw: true,
+        })
+    }
+
+    pub fn window(&self) -> Window {
+        self.window
+    }
+
+    pub fn height(&self) -> u16 {
+        self.height
+    }
+
+    pub fn invalidate(&mut self) {
+        self.needs_redraw = true;
+    }
+
+    pub fn draw<C: Connection>(
+        &mut self,
+        connection: &C,
+        current_tags: u32,
+        occupied_tags: u32,
+    ) -> Result<()> {
+        if !self.needs_redraw {
+            return Ok(());
+        }
+
+        connection.poly_fill_rectangle(
+            self.window,
+            self.graphics_context,
+            &[Rectangle {
+                x: 0,
+                y: 0,
+                width: self.width,
+                height: self.height,
+            }],
+        )?;
+
+        let mut x_position: i16 = 0;
+
+        for (tag_index, tag) in TAGS.iter().enumerate() {
+            let tag_mask = 1 << tag_index;
+            let is_selected = (current_tags & tag_mask) != 0;
+            let is_occupied = (occupied_tags & tag_mask) != 0;
+
+            let tag_width = self.tag_widths[tag_index];
+
+            if is_selected {
+                connection.change_gc(
+                    self.graphics_context,
+                    &ChangeGCAux::new().foreground(0xFFFFFF),
+                )?;
+                connection.poly_fill_rectangle(
+                    self.window,
+                    self.graphics_context,
+                    &[Rectangle {
+                        x: x_position,
+                        y: 0,
+                        width: tag_width,
+                        height: self.height,
+                    }],
+                )?;
+
+                connection.change_gc(
+                    self.graphics_context,
+                    &ChangeGCAux::new().foreground(0x000000),
+                )?;
+            } else if is_occupied {
+                connection.change_gc(
+                    self.graphics_context,
+                    &ChangeGCAux::new().foreground(0x888888),
+                )?;
+                connection.poly_fill_rectangle(
+                    self.window,
+                    self.graphics_context,
+                    &[Rectangle {
+                        x: x_position,
+                        y: 0,
+                        width: tag_width,
+                        height: 2,
+                    }],
+                )?;
+
+                connection.change_gc(
+                    self.graphics_context,
+                    &ChangeGCAux::new().foreground(0xFFFFFF),
+                )?;
+            } else {
+                connection.change_gc(
+                    self.graphics_context,
+                    &ChangeGCAux::new().foreground(0x666666),
+                )?;
+            }
+
+            // TODO: Replace with actual font rendering later
+            connection.image_text8(
+                self.window,
+                self.graphics_context,
+                x_position + 5,
+                self.height as i16 - 5,
+                tag.as_bytes(),
+            )?;
+
+            x_position += tag_width as i16;
+        }
+
+        connection.flush()?;
+        self.needs_redraw = false;
+
+        Ok(())
+    }
+
+    pub fn handle_click(&self, click_x: i16) -> Option<usize> {
+        let mut current_x_position = 0;
+
+        for (tag_index, &tag_width) in self.tag_widths.iter().enumerate() {
+            if click_x >= current_x_position && click_x < current_x_position + tag_width as i16 {
+                return Some(tag_index);
+            }
+            current_x_position += tag_width as i16;
+        }
+        None
+    }
+}
diff --git a/src/bar/mod.rs b/src/bar/mod.rs
new file mode 100644
index 0000000..bac11c9
--- /dev/null
+++ b/src/bar/mod.rs
@@ -0,0 +1,14 @@
+mod bar;
+// mod widgets;  // TODO: implement later
+
+pub use bar::Bar;
+
+// TODO: this should live in config.rs
+pub const BAR_HEIGHT: u16 = 25;
+
+// Bar position (for future use)
+#[derive(Debug, Clone, Copy)]
+pub enum BarPosition {
+    Top,
+    Bottom,
+}
diff --git a/src/config.rs b/src/config.rs
index 146405b..a09609b 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -15,10 +15,20 @@ pub const BORDER_UNFOCUSED: u32 = 0x888888;
 pub const TERMINAL: &str = "alacritty";
 pub const MODKEY: KeyButMask = KeyButMask::MOD1;
 
+// ========================================
+// Commands
+// ========================================
+const SCREENSHOT_CMD: &[&str] = &[
+    "sh",
+    "-c",
+    "maim ~/screenshots/screenshot_$(date +%Y%m%d_%H%M%S).png",
+];
+
 // ========================================
 // TAGS
 // ========================================
 pub const TAG_COUNT: usize = 9;
+pub const TAGS: [&str; TAG_COUNT] = ["1", "2", "3", "4", "5", "6", "7", "8", "9"];
 
 // ========================================
 // KEYBINDINGS
@@ -26,7 +36,8 @@ pub const TAG_COUNT: usize = 9;
 #[rustfmt::skip]
 pub const KEYBINDINGS: &[Key] = &[
     Key::new(&[MODKEY],        keycodes::RETURN, KeyAction::Spawn,      Arg::Str(TERMINAL)),
-    
+
+    Key::new(&[MODKEY],        keycodes::S,      KeyAction::Spawn,      Arg::Array(SCREENSHOT_CMD)),
     Key::new(&[MODKEY],        keycodes::Q,      KeyAction::KillClient, Arg::None),
     Key::new(&[MODKEY, SHIFT], keycodes::Q,      KeyAction::Quit,       Arg::None),
     Key::new(&[MODKEY],        keycodes::J,      KeyAction::FocusStack, Arg::Int(-1)),
diff --git a/src/keyboard/handlers.rs b/src/keyboard/handlers.rs
index 0dbeba1..294c724 100644
--- a/src/keyboard/handlers.rs
+++ b/src/keyboard/handlers.rs
@@ -16,9 +16,10 @@ pub enum KeyAction {
 
 #[derive(Debug)]
 pub enum Arg {
-    Str(&'static str),
-    Int(i32),
     None,
+    Int(i32),
+    Str(&'static str),
+    Array(&'static [&'static str]),
 }
 
 pub struct Key {
diff --git a/src/main.rs b/src/main.rs
index b59dc4a..2b0cfa7 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,4 +1,5 @@
 use anyhow::Result;
+mod bar;
 mod config;
 mod keyboard;
 mod layout;
diff --git a/src/window_manager.rs b/src/window_manager.rs
index 0e7ec61..b53fbca 100644
--- a/src/window_manager.rs
+++ b/src/window_manager.rs
@@ -1,3 +1,4 @@
+use crate::bar::Bar;
 use crate::config::{BORDER_FOCUSED, BORDER_UNFOCUSED, BORDER_WIDTH, TAG_COUNT};
 use crate::keyboard::{self, Arg, KeyAction};
 use crate::layout::Layout;
@@ -24,6 +25,7 @@ pub struct WindowManager {
     layout: Box<dyn Layout>,
     window_tags: std::collections::HashMap<Window, TagMask>,
     selected_tags: TagMask,
+    bar: Bar,
 }
 
 impl WindowManager {
@@ -44,6 +46,8 @@ impl WindowManager {
             )?
             .check()?;
 
+        let bar = Bar::new(&connection, &screen)?;
+
         return Ok(Self {
             connection,
             screen_number,
@@ -54,6 +58,7 @@ impl WindowManager {
             layout: Box::new(TilingLayout),
             window_tags: std::collections::HashMap::new(),
             selected_tags: tag_mask(0),
+            bar,
         });
     }
 
@@ -62,19 +67,40 @@ impl WindowManager {
 
         keyboard::setup_keybinds(&self.connection, self.root)?;
 
+        // Initial bar draw
+        self.update_bar()?;
+
         loop {
             let event = self.connection.wait_for_event()?;
             self.handle_event(event)?;
         }
     }
 
+    fn update_bar(&mut self) -> Result<()> {
+        let mut occupied_tags: TagMask = 0;
+        for &tags in self.window_tags.values() {
+            occupied_tags |= tags;
+        }
+
+        self.bar.invalidate();
+        self.bar
+            .draw(&self.connection, self.selected_tags, occupied_tags)?;
+        Ok(())
+    }
+
     fn handle_key_action(&mut self, action: KeyAction, arg: &Arg) -> Result<()> {
         match action {
-            KeyAction::Spawn => {
-                if let Arg::Str(command) = arg {
+            KeyAction::Spawn => match arg {
+                Arg::Str(command) => {
                     std::process::Command::new(command).spawn()?;
                 }
-            }
+                Arg::Array(cmd) => {
+                    if let Some((program, args)) = cmd.split_first() {
+                        std::process::Command::new(program).args(args).spawn()?;
+                    }
+                }
+                _ => {}
+            },
             KeyAction::KillClient => {
                 if let Some(focused) = self.focused_window {
                     match self.connection.kill_client(focused) {
@@ -148,6 +174,7 @@ impl WindowManager {
         self.selected_tags = tag_mask(tag_index);
         self.update_window_visibility()?;
         self.apply_layout()?;
+        self.update_bar()?; // Update bar to show new tag
 
         let visible = self.visible_windows();
         self.set_focus(visible.first().copied())?;
@@ -165,6 +192,7 @@ impl WindowManager {
             self.window_tags.insert(focused, mask);
             self.update_window_visibility()?;
             self.apply_layout()?;
+            self.update_bar()?; // Update bar to show occupied tags changed
         }
 
         Ok(())
@@ -238,6 +266,7 @@ impl WindowManager {
                 self.windows.push(event.window);
                 self.window_tags.insert(event.window, self.selected_tags);
                 self.apply_layout()?;
+                self.update_bar()?;
                 self.set_focus(Some(event.window))?;
             }
             Event::UnmapNotify(event) => {
@@ -245,13 +274,6 @@ impl WindowManager {
                     self.remove_window(event.window)?;
                 }
             }
-            // Event::UnmapNotify(event) => {
-            //     if self.windows.contains(&event.window) {
-            //         if self.is_window_visible(event.window) {
-            //             self.remove_window(event.window)?;
-            //         }
-            //     }
-            // }
             Event::DestroyNotify(event) => {
                 if self.windows.contains(&event.window) {
                     self.remove_window(event.window)?;
@@ -261,6 +283,20 @@ impl WindowManager {
                 let (action, arg) = keyboard::handle_key_press(event)?;
                 self.handle_key_action(action, arg)?;
             }
+            Event::ButtonPress(event) => {
+                // Check if click was on the bar
+                if event.event == self.bar.window() {
+                    if let Some(tag_index) = self.bar.handle_click(event.event_x) {
+                        self.view_tag(tag_index)?;
+                    }
+                }
+            }
+            Event::Expose(event) => {
+                if event.window == self.bar.window() {
+                    self.bar.invalidate();
+                    self.update_bar()?;
+                }
+            }
             _ => {}
         }
         Ok(())
@@ -271,18 +307,23 @@ impl WindowManager {
         let screen_height = self.screen.height_in_pixels as u32;
         let border_width = BORDER_WIDTH;
 
+        let bar_height = self.bar.height() as u32;
+        let usable_height = screen_height.saturating_sub(bar_height);
+
         let visible = self.visible_windows();
-        let geometries = self.layout.arrange(&visible, screen_width, screen_height);
+        let geometries = self.layout.arrange(&visible, screen_width, usable_height);
 
         for (window, geometry) in visible.iter().zip(geometries.iter()) {
             let adjusted_width = geometry.width.saturating_sub(2 * border_width);
             let adjusted_height = geometry.height.saturating_sub(2 * border_width);
 
+            let adjusted_y = geometry.y_coordinate + bar_height as i32;
+
             self.connection.configure_window(
                 *window,
                 &ConfigureWindowAux::new()
                     .x(geometry.x_coordinate)
-                    .y(geometry.y_coordinate)
+                    .y(adjusted_y)
                     .width(adjusted_width)
                     .height(adjusted_height),
             )?;
@@ -307,6 +348,7 @@ impl WindowManager {
             }
 
             self.apply_layout()?;
+            self.update_bar()?;
         }
         Ok(())
     }