diff --git a/book/src/configuration/shortcuts.md b/book/src/configuration/shortcuts.md index 465c2bb8..6d372be6 100644 --- a/book/src/configuration/shortcuts.md +++ b/book/src/configuration/shortcuts.md @@ -97,10 +97,10 @@ alt-shift-l = "move-right" ```toml [shortcuts] -alt-d = "split-horizontal" -alt-v = "split-vertical" -alt-t = "toggle-split" -alt-m = "toggle-mono" +alt-d = "make-group-h" +alt-v = "make-group-v" +alt-t = "change-group-opposite" +alt-m = "toggle-tab" alt-f = "focus-parent" ``` @@ -475,7 +475,7 @@ table and reference them with `$name`: ```toml [actions] my-layout = [ - "split-horizontal", + "make-group-h", { type = "exec", exec = "alacritty" }, ] @@ -535,10 +535,9 @@ mode = { When certain simple actions are used inside a [window rule](../window-rules.md), they apply to the **matched window** instead of the focused window. The affected actions are: `move-left`, `move-down`, `move-up`, `move-right`, -`split-horizontal`, `split-vertical`, `toggle-split`, `tile-horizontal`, -`tile-vertical`, `show-single`, `show-all`, `toggle-fullscreen`, -`enter-fullscreen`, `exit-fullscreen`, `close`, `toggle-floating`, `float`, -`tile`, `toggle-float-pinned`, `pin-float`, `unpin-float`, and `resize`. +`toggle-fullscreen`, `enter-fullscreen`, `exit-fullscreen`, `close`, +`toggle-floating`, `float`, `tile`, `toggle-float-pinned`, `pin-float`, +`unpin-float`, and `resize`. Similarly, `kill-client` applies to the matched window's client in a window rule, or to the matched client in a client rule. diff --git a/book/src/tiling.md b/book/src/tiling.md index 5db527b2..650cd73a 100644 --- a/book/src/tiling.md +++ b/book/src/tiling.md @@ -4,32 +4,20 @@ Jay uses an i3-like tiling layout. Windows are arranged automatically in containers that can be split horizontally or vertically. Containers can be nested to create complex layouts. -## Splitting Containers +## Grouping Containers -When you split a window, Jay wraps it in a new container with the specified -direction. Subsequent windows opened in that container are placed side by side -(horizontal split) or stacked top to bottom (vertical split). +When you group a window, Jay wraps it in a new container with the specified +direction. Subsequent windows opened in that group are placed side by side +(horizontal group) or stacked top to bottom (vertical group). -`alt-d` -- `split-horizontal` -: Split the focused window horizontally +`alt-d` -- `make-group-h` +: Group the focused window horizontally -`alt-v` -- `split-vertical` -: Split the focused window vertically +`alt-v` -- `make-group-v` +: Group the focused window vertically -`alt-t` -- `toggle-split` -: Toggle the container's split direction - -You can also set the direction explicitly without toggling: - -```toml -[shortcuts] -alt-shift-d = "tile-horizontal" -alt-shift-v = "tile-vertical" -``` - -The `tile-horizontal` action sets the container to horizontal, and -`tile-vertical` sets it to vertical -- unlike `split-horizontal`/`split-vertical` -which wrap the window in a new container first. +`alt-t` -- `change-group-opposite` +: Toggle the current group's direction ## Moving Focus @@ -81,22 +69,14 @@ windows at once. For example, focusing a parent container and then using By default, a container shows all its children side by side. Mono mode changes this so only one child is visible at a time, similar to a tabbed view. -`alt-m` -- `toggle-mono` -: Toggle between mono and side-by-side +`alt-m` -- `toggle-tab` +: Toggle between tabbed and side-by-side You can also right-click any title in a container to toggle mono mode. In mono mode, scroll over the title bar to cycle between windows in the container. -For explicit control without toggling: - -```toml -[shortcuts] -alt-s = "show-single" # Enter mono mode -alt-a = "show-all" # Exit mono mode -``` - ## Fullscreen Press `alt-u` (`toggle-fullscreen`) to make the focused window fill the entire @@ -137,20 +117,17 @@ Double-click a tile's title bar to toggle it between tiled and floating. See ## Summary of Tiling Actions -`split-horizontal` -: Wrap focused window in a horizontal container +`make-group-h` +: Wrap focused window in a horizontal group -`split-vertical` -: Wrap focused window in a vertical container +`make-group-v` +: Wrap focused window in a vertical group -`toggle-split` -: Toggle container split direction +`make-group-tab` +: Wrap focused window in a tabbed group -`tile-horizontal` -: Set container direction to horizontal - -`tile-vertical` -: Set container direction to vertical +`change-group-opposite` +: Toggle group direction `focus-left/right/up/down` : Move keyboard focus @@ -161,14 +138,8 @@ Double-click a tile's title bar to toggle it between tiled and floating. See `focus-parent` : Focus the parent container -`toggle-mono` -: Toggle mono mode - -`show-single` -: Enter mono mode - -`show-all` -: Exit mono mode +`toggle-tab` +: Toggle tabbed mode `toggle-fullscreen` : Toggle fullscreen diff --git a/src/text.rs b/src/text.rs index 0faa59c3..3efd5c3d 100644 --- a/src/text.rs +++ b/src/text.rs @@ -92,6 +92,23 @@ impl<'a> Config<'a> { markup, scale, }, + Config::RenderFittingOrEllipsized { + height, + max_width, + font, + text, + color, + markup, + scale, + } => Config::RenderFittingOrEllipsized { + height, + max_width, + font, + text: text.into_owned().into(), + color, + markup, + scale, + }, Config::Render { x, y, @@ -262,6 +279,26 @@ fn render_fitting( ) } +fn render_fitting_or_ellipsized( + memfd: &Memfd, + height: Option, + max_width: i32, + font: &str, + text: &str, + color: Color, + markup: bool, + scale: Option, +) -> Result { + let measurement = measure(memfd, font, text, markup, scale)?; + if measurement.ink_rect.width() <= max_width { + return render_fitting(memfd, height, font, text, color, markup, scale); + } + let height = height.unwrap_or(measurement.ink_rect.height()); + render( + memfd, 0, None, max_width, height, 0, font, text, color, true, markup, scale, + ) +} + #[derive(Debug, Copy, Clone, Default)] pub struct TextMeasurement { pub ink_rect: Rect, @@ -303,6 +340,24 @@ impl RenderWork { markup, scale, } => render_fitting(&self.memfd, height, font, text, color, markup, scale), + Config::RenderFittingOrEllipsized { + height, + max_width, + ref font, + ref text, + color, + markup, + scale, + } => render_fitting_or_ellipsized( + &self.memfd, + height, + max_width, + font, + text, + color, + markup, + scale, + ), Config::Render { x, y, @@ -428,6 +483,15 @@ enum Config<'a> { markup: bool, scale: Option, }, + RenderFittingOrEllipsized { + height: Option, + max_width: i32, + font: Arc, + text: Cow<'a, str>, + color: Color, + markup: bool, + scale: Option, + }, Render { x: i32, y: Option, @@ -573,6 +637,29 @@ impl TextTexture { self.apply_config(on_completed, config) } + pub fn schedule_render_fitting_or_ellipsized( + &self, + on_completed: Rc, + height: Option, + max_width: i32, + font: &Arc, + text: &str, + color: Color, + markup: bool, + scale: Option, + ) { + let config = Config::RenderFittingOrEllipsized { + height, + max_width, + font: font.clone(), + text: text.into(), + color, + markup, + scale, + }; + self.apply_config(on_completed, config) + } + pub fn flip(&self) -> Result<(), TextError> { let res = self .data diff --git a/src/tree/container.rs b/src/tree/container.rs index fe28042c..61ec00d1 100644 --- a/src/tree/container.rs +++ b/src/tree/container.rs @@ -937,14 +937,29 @@ impl ContainerNode { .persistent .scale .get(); + let old_textures: AHashMap<_, _> = self + .tab_bar + .borrow() + .as_ref() + .map(|bar| { + bar.entries + .iter() + .map(|entry| (entry.child_id, entry.title_texture.clone())) + .collect() + }) + .unwrap_or_default(); let mut bar = TabBar::new(height, render_scale); for child in self.children.iter() { let child_id = child.node.node_id(); let title = self.get_child_tab_title(&child, override_id, override_title); + let title_texture = old_textures + .get(&child_id) + .cloned() + .unwrap_or_else(|| Rc::new(RefCell::new(None))); bar.entries.push(TabBarEntry { child_id, title, - title_texture: Rc::new(RefCell::new(None)), + title_texture, active: child_id == active_id, attention_requested: child.attention_requested.get(), x: Cell::new(0), @@ -964,10 +979,27 @@ impl ContainerNode { let Some(focused) = self.focus_history.last() else { return; }; - if self.num_children.get() <= 1 { + let focused_node = focused.node.clone(); + let focused_active = focused_node.tl_data().active(); + if self.num_children.get() == 1 { + let sub = ContainerNode::new( + &self.state, + &self.workspace.get(), + focused_node.clone(), + split, + ); + let sub_id = sub.node_id(); + if ephemeral { + sub.ephemeral.set(Ephemeral::On); + } + self.clone().cnode_replace_child(&*focused_node, sub); + if focused_active + && let Some(group) = self.child_nodes.borrow().get(&sub_id).map(|n| n.to_ref()) + { + self.update_child_active(&group, true, 1); + } return; } - let focused_node = focused.node.clone(); // Record the sibling that comes AFTER the focused child so we can // insert the new group at the same position. let next_sibling: Option> = { @@ -1774,6 +1806,7 @@ impl ContainerNode { e.title.clone(), TabBar::entry_colors(&self.state, e), e.title_texture.clone(), + e.width.get(), ) }) .collect(); @@ -1794,12 +1827,24 @@ impl ContainerNode { texture_height = (bar_height as f64 * s).round() as _; } let mut texture_refs = Vec::new(); - for (title, (_, _, text_color), title_texture) in &entries { + let text_padding = self.state.theme.sizes.tab_bar_text_padding.get(); + let border_width = self.state.theme.sizes.tab_bar_border_width.get(); + for (title, (_, _, text_color), title_texture, tab_width) in &entries { + let max_width = (*tab_width - 2 * (text_padding + border_width)).max(0); + let max_width = if let Some(s) = scale { + (max_width as f64 * s).round() as i32 + } else { + max_width + }; + if max_width <= 0 { + continue; + } let mut tex_ref = title_texture.borrow_mut(); let tex = tex_ref.get_or_insert_with(|| TextTexture::new(&self.state, &ctx)); - tex.schedule_render_fitting( + tex.schedule_render_fitting_or_ellipsized( on_completed.clone(), Some(texture_height), + max_width, &font, title, *text_color, @@ -2465,6 +2510,7 @@ impl ToplevelNodeBase for ContainerNode { let padding = self.state.theme.sizes.tab_bar_padding.get(); bar.layout_entries(rect.width(), padding); } + self.schedule_update_tab_textures(); } // log::info!("tl_change_extents"); self.perform_layout(); diff --git a/toml-config/src/default-config.toml b/toml-config/src/default-config.toml index fd572891..2b51e526 100644 --- a/toml-config/src/default-config.toml +++ b/toml-config/src/default-config.toml @@ -23,11 +23,11 @@ alt-shift-j = "move-down" alt-shift-k = "move-up" alt-shift-l = "move-right" -alt-d = "split-horizontal" -alt-v = "split-vertical" +alt-d = "make-group-h" +alt-v = "make-group-v" -alt-t = "toggle-split" -alt-m = "toggle-mono" +alt-t = "change-group-opposite" +alt-m = "toggle-tab" alt-u = "toggle-fullscreen" alt-f = "focus-parent" diff --git a/toml-spec/spec/spec.generated.json b/toml-spec/spec/spec.generated.json index 62527696..930ad697 100644 --- a/toml-spec/spec/spec.generated.json +++ b/toml-spec/spec/spec.generated.json @@ -930,7 +930,7 @@ "required": [] }, "Config": { - "description": "This is the top-level table.\n\n- Example:\n\n ```toml\n keymap = \"\"\"\n xkb_keymap {\n xkb_keycodes { include \"evdev+aliases(qwerty)\" };\n xkb_types { include \"complete\" };\n xkb_compat { include \"complete\" };\n xkb_symbols { include \"pc+us+inet(evdev)\" };\n };\n \"\"\"\n\n on-graphics-initialized = { type = \"exec\", exec = \"mako\" }\n\n [shortcuts]\n alt-h = \"focus-left\"\n alt-j = \"focus-down\"\n alt-k = \"focus-up\"\n alt-l = \"focus-right\"\n\n alt-shift-h = \"move-left\"\n alt-shift-j = \"move-down\"\n alt-shift-k = \"move-up\"\n alt-shift-l = \"move-right\"\n\n alt-d = \"split-horizontal\"\n alt-v = \"split-vertical\"\n\n alt-t = \"toggle-split\"\n alt-m = \"toggle-mono\"\n alt-u = \"toggle-fullscreen\"\n\n alt-f = \"focus-parent\"\n alt-shift-c = \"close\"\n alt-shift-f = \"toggle-floating\"\n Super_L = { type = \"exec\", exec = \"alacritty\" }\n alt-p = { type = \"exec\", exec = \"bemenu-run\" }\n alt-q = \"quit\"\n alt-shift-r = \"reload-config-toml\"\n\n ctrl-alt-F1 = { type = \"switch-to-vt\", num = 1 }\n ctrl-alt-F2 = { type = \"switch-to-vt\", num = 2 }\n # ...\n\n alt-F1 = { type = \"show-workspace\", name = \"1\" }\n alt-F2 = { type = \"show-workspace\", name = \"2\" }\n # ...\n\n alt-shift-F1 = { type = \"move-to-workspace\", name = \"1\" }\n alt-shift-F2 = { type = \"move-to-workspace\", name = \"2\" }\n # ...\n ```\n", + "description": "This is the top-level table.\n\n- Example:\n\n ```toml\n keymap = \"\"\"\n xkb_keymap {\n xkb_keycodes { include \"evdev+aliases(qwerty)\" };\n xkb_types { include \"complete\" };\n xkb_compat { include \"complete\" };\n xkb_symbols { include \"pc+us+inet(evdev)\" };\n };\n \"\"\"\n\n on-graphics-initialized = { type = \"exec\", exec = \"mako\" }\n\n [shortcuts]\n alt-h = \"focus-left\"\n alt-j = \"focus-down\"\n alt-k = \"focus-up\"\n alt-l = \"focus-right\"\n\n alt-shift-h = \"move-left\"\n alt-shift-j = \"move-down\"\n alt-shift-k = \"move-up\"\n alt-shift-l = \"move-right\"\n\n alt-d = \"make-group-h\"\n alt-v = \"make-group-v\"\n\n alt-t = \"change-group-opposite\"\n alt-m = \"toggle-tab\"\n alt-u = \"toggle-fullscreen\"\n\n alt-f = \"focus-parent\"\n alt-shift-c = \"close\"\n alt-shift-f = \"toggle-floating\"\n Super_L = { type = \"exec\", exec = \"alacritty\" }\n alt-p = { type = \"exec\", exec = \"bemenu-run\" }\n alt-q = \"quit\"\n alt-shift-r = \"reload-config-toml\"\n\n ctrl-alt-F1 = { type = \"switch-to-vt\", num = 1 }\n ctrl-alt-F2 = { type = \"switch-to-vt\", num = 2 }\n # ...\n\n alt-F1 = { type = \"show-workspace\", name = \"1\" }\n alt-F2 = { type = \"show-workspace\", name = \"2\" }\n # ...\n\n alt-shift-F1 = { type = \"move-to-workspace\", name = \"1\" }\n alt-shift-F2 = { type = \"move-to-workspace\", name = \"2\" }\n # ...\n ```\n", "type": "object", "properties": { "keymap": { @@ -1993,7 +1993,7 @@ }, "SimpleActionName": { "type": "string", - "description": "The name of a `simple` Action.\n\nWhen used inside a window rule, the following actions apply to the matched window\ninstead fo the focused window:\n\n- `move-left`\n- `move-down`\n- `move-up`\n- `move-right`\n- `split-horizontal`\n- `split-vertical`\n- `toggle-split`\n- `tile-horizontal`\n- `tile-vertical`\n- `toggle-split`\n- `show-single`\n- `show-all`\n- `toggle-fullscreen`\n- `enter-fullscreen`\n- `exit-fullscreen`\n- `close`\n- `toggle-floating`\n- `float`\n- `tile`\n- `toggle-float-pinned`\n- `pin-float`\n- `unpin-float`\n\n\n- Example:\n\n ```toml\n [shortcuts]\n alt-q = \"quit\"\n ```\n", + "description": "The name of a `simple` Action.\n\nWhen used inside a window rule, the following actions apply to the matched window\ninstead fo the focused window:\n\n- `move-left`\n- `move-down`\n- `move-up`\n- `move-right`\n- `toggle-fullscreen`\n- `enter-fullscreen`\n- `exit-fullscreen`\n- `close`\n- `toggle-floating`\n- `float`\n- `tile`\n- `toggle-float-pinned`\n- `pin-float`\n- `unpin-float`\n\n\n- Example:\n\n ```toml\n [shortcuts]\n alt-q = \"quit\"\n ```\n", "enum": [ "focus-left", "focus-down", @@ -2004,14 +2004,11 @@ "move-up", "move-right", "move-right", - "split-horizontal", - "split-vertical", - "toggle-split", - "tile-horizontal", - "tile-vertical", - "toggle-mono", - "show-single", - "show-all", + "make-group-h", + "make-group-v", + "make-group-tab", + "change-group-opposite", + "toggle-tab", "toggle-fullscreen", "enter-fullscreen", "exit-fullscreen", diff --git a/toml-spec/spec/spec.generated.md b/toml-spec/spec/spec.generated.md index 9f455c69..43e9f20d 100644 --- a/toml-spec/spec/spec.generated.md +++ b/toml-spec/spec/spec.generated.md @@ -1659,11 +1659,11 @@ This is the top-level table. alt-shift-k = "move-up" alt-shift-l = "move-right" - alt-d = "split-horizontal" - alt-v = "split-vertical" + alt-d = "make-group-h" + alt-v = "make-group-v" - alt-t = "toggle-split" - alt-m = "toggle-mono" + alt-t = "change-group-opposite" + alt-m = "toggle-tab" alt-u = "toggle-fullscreen" alt-f = "focus-parent" @@ -4397,14 +4397,6 @@ instead fo the focused window: - `move-down` - `move-up` - `move-right` -- `split-horizontal` -- `split-vertical` -- `toggle-split` -- `tile-horizontal` -- `tile-vertical` -- `toggle-split` -- `show-single` -- `show-all` - `toggle-fullscreen` - `enter-fullscreen` - `exit-fullscreen` @@ -4464,38 +4456,25 @@ The string should have one of the following values: Move the currently focused window one to the right. -- `split-horizontal`: +- `make-group-h`: - Split the currently focused window horizontally. + Wraps the focused window in a horizontal group. -- `split-vertical`: +- `make-group-v`: - Split the currently focused window vertically. + Wraps the focused window in a vertical group. -- `toggle-split`: +- `make-group-tab`: - Toggle the split of the currently focused container between vertical and - horizontal. + Wraps the focused window in a tabbed group. -- `tile-horizontal`: +- `change-group-opposite`: - Sets the split of the currently focused container to horizontal. + Toggles the current group's direction. -- `tile-vertical`: +- `toggle-tab`: - Sets the split of the currently focused container to vertical. - -- `toggle-mono`: - - Toggle the currently focused container between showing a single and all children. - -- `show-single`: - - Makes the currently focused container show a single child. - -- `show-all`: - - Makes the currently focused container show all children. + Toggles the current group between tabbed and split mode. - `toggle-fullscreen`: diff --git a/toml-spec/spec/spec.yaml b/toml-spec/spec/spec.yaml index 3bb0ebbb..aa6789da 100644 --- a/toml-spec/spec/spec.yaml +++ b/toml-spec/spec/spec.yaml @@ -1016,14 +1016,6 @@ SimpleActionName: - `move-down` - `move-up` - `move-right` - - `split-horizontal` - - `split-vertical` - - `toggle-split` - - `tile-horizontal` - - `tile-vertical` - - `toggle-split` - - `show-single` - - `show-all` - `toggle-fullscreen` - `enter-fullscreen` - `exit-fullscreen` @@ -1062,25 +1054,16 @@ SimpleActionName: description: Move the currently focused window one to the right. - value: move-right description: Move the currently focused window one to the right. - - value: split-horizontal - description: Split the currently focused window horizontally. - - value: split-vertical - description: Split the currently focused window vertically. - - value: toggle-split - description: | - Toggle the split of the currently focused container between vertical and - horizontal. - - value: tile-horizontal - description: Sets the split of the currently focused container to horizontal. - - value: tile-vertical - description: Sets the split of the currently focused container to vertical. - - value: toggle-mono - description: | - Toggle the currently focused container between showing a single and all children. - - value: show-single - description: Makes the currently focused container show a single child. - - value: show-all - description: Makes the currently focused container show all children. + - value: make-group-h + description: Wraps the focused window in a horizontal group. + - value: make-group-v + description: Wraps the focused window in a vertical group. + - value: make-group-tab + description: Wraps the focused window in a tabbed group. + - value: change-group-opposite + description: Toggles the current group's direction. + - value: toggle-tab + description: Toggles the current group between tabbed and split mode. - value: toggle-fullscreen description: Toggle the currently focused window between fullscreen and windowed. - value: enter-fullscreen @@ -2467,11 +2450,11 @@ Config: alt-shift-k = "move-up" alt-shift-l = "move-right" - alt-d = "split-horizontal" - alt-v = "split-vertical" + alt-d = "make-group-h" + alt-v = "make-group-v" - alt-t = "toggle-split" - alt-m = "toggle-mono" + alt-t = "change-group-opposite" + alt-m = "toggle-tab" alt-u = "toggle-fullscreen" alt-f = "focus-parent"