mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2025-01-13 08:01:05 +01:00
Simple floating window support
This commit is contained in:
parent
3ea2452397
commit
71f71feaf2
10 changed files with 98 additions and 60 deletions
|
@ -10,4 +10,14 @@ function M.close_window(client_id)
|
|||
})
|
||||
end
|
||||
|
||||
---Toggle a window's floating status.
|
||||
---@param client_id integer? The id of the window you want to toggle, or nil to toggle the currently focused window, if any.
|
||||
function M.toggle_floating(client_id)
|
||||
SendMsg({
|
||||
ToggleFloating = {
|
||||
client_id = client_id or "nil",
|
||||
},
|
||||
})
|
||||
end
|
||||
|
||||
return M
|
||||
|
|
|
@ -3,7 +3,7 @@ local M = {}
|
|||
---Set a keybind. If called on an already existing keybind, it gets replaced.
|
||||
---@param key Keys The key for the keybind. NOTE: uppercase and lowercase characters are considered different.
|
||||
---@param modifiers Modifiers[] Which modifiers need to be pressed for the keybind to trigger.
|
||||
---@param action function What to run.
|
||||
---@param action fun() What to run.
|
||||
function M.keybind(modifiers, key, action)
|
||||
table.insert(CallbackTable, action)
|
||||
SendMsg({
|
||||
|
|
|
@ -14,6 +14,9 @@ pub enum Msg {
|
|||
CloseWindow {
|
||||
client_id: Option<u32>,
|
||||
},
|
||||
ToggleFloating {
|
||||
client_id: Option<u32>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||
|
|
23
src/focus.rs
23
src/focus.rs
|
@ -1,9 +1,4 @@
|
|||
use smithay::{
|
||||
desktop::Window,
|
||||
utils::{IsAlive, Serial},
|
||||
};
|
||||
|
||||
use crate::{backend::Backend, state::State};
|
||||
use smithay::{desktop::Window, utils::IsAlive};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct FocusState {
|
||||
|
@ -30,19 +25,3 @@ impl FocusState {
|
|||
self.focus_stack.push(window);
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Backend> State<B> {
|
||||
pub fn set_focus(&mut self, window: Window, serial: Serial) {
|
||||
// INFO: this is inserted into the loop because foot didn't like it when you set the focus
|
||||
// |` immediately after creating the toplevel
|
||||
// TODO: figure out why
|
||||
self.loop_handle.insert_idle(move |data| {
|
||||
data.state.focus_state.set_focus(window.clone());
|
||||
data.state.seat.get_keyboard().unwrap().set_focus(
|
||||
&mut data.state,
|
||||
Some(window.toplevel().wl_surface().clone()),
|
||||
serial,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -177,7 +177,13 @@ impl<B: Backend> SeatHandler for State<B> {
|
|||
self.cursor_status = image;
|
||||
}
|
||||
|
||||
fn focus_changed(&mut self, _seat: &Seat<Self>, _focused: Option<&Self::KeyboardFocus>) {}
|
||||
fn focus_changed(&mut self, _seat: &Seat<Self>, focused: Option<&Self::KeyboardFocus>) {
|
||||
if let Some(wl_surface) = focused {
|
||||
if let Some(window) = self.window_for_surface(wl_surface) {
|
||||
self.focus_state.set_focus(window);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
delegate_seat!(@<B: Backend> State<B>);
|
||||
|
||||
|
@ -195,8 +201,15 @@ impl<B: Backend> XdgShellHandler for State<B> {
|
|||
|
||||
fn new_toplevel(&mut self, surface: ToplevelSurface) {
|
||||
let window = Window::new(surface);
|
||||
|
||||
self.space.map_element(window.clone(), (0, 0), true);
|
||||
self.set_focus(window, SERIAL_COUNTER.next_serial());
|
||||
self.loop_handle.insert_idle(move |data| {
|
||||
data.state.seat.get_keyboard().unwrap().set_focus(
|
||||
&mut data.state,
|
||||
Some(window.toplevel().wl_surface().clone()),
|
||||
SERIAL_COUNTER.next_serial(),
|
||||
)
|
||||
});
|
||||
let windows: Vec<Window> = self.space.elements().cloned().collect();
|
||||
|
||||
Layout::master_stack(self, windows, crate::layout::Direction::Left);
|
||||
|
|
|
@ -131,7 +131,7 @@ impl<B: Backend> State<B> {
|
|||
// Move window to top of stack.
|
||||
self.space.raise_element(&window, true);
|
||||
|
||||
self.set_focus(window, serial);
|
||||
keyboard.set_focus(self, Some(window.toplevel().wl_surface().clone()), serial);
|
||||
|
||||
self.space.elements().for_each(|window| {
|
||||
window.toplevel().send_configure();
|
||||
|
|
|
@ -1,14 +1,24 @@
|
|||
use smithay::desktop::Window;
|
||||
|
||||
use crate::{backend::Backend, state::State};
|
||||
use crate::{
|
||||
backend::Backend,
|
||||
state::State,
|
||||
window::window_state::{Float, WindowState},
|
||||
};
|
||||
|
||||
use super::{Direction, Layout};
|
||||
|
||||
impl Layout {
|
||||
pub fn master_stack<B: Backend>(state: &mut State<B>, windows: Vec<Window>, side: Direction) {
|
||||
pub fn master_stack<B: Backend>(
|
||||
state: &mut State<B>,
|
||||
mut windows: Vec<Window>,
|
||||
side: Direction,
|
||||
) {
|
||||
windows.retain(|win| {
|
||||
WindowState::with_state(win, |state| matches!(state.floating, Float::Tiled(_)))
|
||||
});
|
||||
match side {
|
||||
Direction::Left => {
|
||||
// println!("MasterStack layout_windows");
|
||||
let window_count = windows.len();
|
||||
if window_count == 0 {
|
||||
return;
|
||||
|
|
15
src/state.rs
15
src/state.rs
|
@ -127,17 +127,16 @@ impl<B: Backend> State<B> {
|
|||
Msg::SetMousebind { button } => todo!(),
|
||||
Msg::CloseWindow { client_id } => {
|
||||
tracing::info!("CloseWindow {:?}", client_id);
|
||||
if let Some(window) = data
|
||||
.state
|
||||
.seat
|
||||
.get_keyboard()
|
||||
.unwrap()
|
||||
.current_focus()
|
||||
.and_then(|wl_surface| data.state.window_for_surface(&wl_surface))
|
||||
{
|
||||
if let Some(window) = data.state.focus_state.current_focus() {
|
||||
window.toplevel().send_close();
|
||||
}
|
||||
}
|
||||
Msg::ToggleFloating { client_id } => {
|
||||
// TODO: add client_ids
|
||||
if let Some(window) = data.state.focus_state.current_focus() {
|
||||
crate::window::toggle_floating(&mut data.state, &window);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
Event::Closed => todo!(),
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
use std::cell::RefCell;
|
||||
|
||||
use smithay::{reexports::wayland_server::protocol::wl_surface::WlSurface, wayland::compositor};
|
||||
use smithay::{
|
||||
desktop::Window, reexports::wayland_server::protocol::wl_surface::WlSurface,
|
||||
wayland::compositor,
|
||||
};
|
||||
|
||||
use crate::{backend::Backend, state::State};
|
||||
use crate::{backend::Backend, layout::Layout, state::State};
|
||||
|
||||
use self::window_state::{Float, WindowState};
|
||||
|
||||
|
@ -23,32 +26,35 @@ pub trait SurfaceState: Default + 'static {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn toggle_floating<B: Backend>(state: &mut State<B>, wl_surface: &WlSurface) {
|
||||
WindowState::with_state(wl_surface, |window_state| {
|
||||
let window = state.window_for_surface(wl_surface).unwrap();
|
||||
pub fn toggle_floating<B: Backend>(state: &mut State<B>, window: &Window) {
|
||||
tracing::info!("toggling floating");
|
||||
WindowState::with_state(window, |window_state| {
|
||||
match window_state.floating {
|
||||
Float::NotFloating(prev_loc_and_size) => {
|
||||
Float::Tiled(prev_loc_and_size) => {
|
||||
if let Some((prev_loc, prev_size)) = prev_loc_and_size {
|
||||
tracing::info!("changing size and loc");
|
||||
window.toplevel().with_pending_state(|state| {
|
||||
state.size = Some(prev_size);
|
||||
});
|
||||
|
||||
window.toplevel().send_pending_configure();
|
||||
|
||||
state.space.map_element(window, prev_loc, false); // TODO: should it activate?
|
||||
state.space.map_element(window.clone(), prev_loc, false); // TODO: should it activate?
|
||||
}
|
||||
|
||||
window_state.floating = Float::Floating
|
||||
window_state.floating = Float::Floating;
|
||||
}
|
||||
Float::Floating => {
|
||||
// TODO: recompute all non-floating window positions
|
||||
|
||||
window_state.floating = Float::NotFloating(Some((
|
||||
state.space.element_location(&window).unwrap(), // We get the location this way
|
||||
window_state.floating = Float::Tiled(Some((
|
||||
state.space.element_location(window).unwrap(), // We get the location this way
|
||||
// because window.geometry().loc doesn't seem to be the actual location
|
||||
window.geometry().size,
|
||||
)));
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
let windows = state.space.elements().cloned().collect::<Vec<_>>();
|
||||
Layout::master_stack(state, windows, crate::layout::Direction::Left);
|
||||
state.space.raise_element(window, true);
|
||||
}
|
||||
|
|
|
@ -1,30 +1,48 @@
|
|||
use smithay::utils::{Logical, Point, Size};
|
||||
use std::{borrow::BorrowMut, cell::RefCell};
|
||||
|
||||
use super::SurfaceState;
|
||||
use smithay::{
|
||||
desktop::Window,
|
||||
utils::{Logical, Point, Size},
|
||||
};
|
||||
|
||||
pub struct WindowState {
|
||||
pub floating: Float,
|
||||
}
|
||||
|
||||
pub enum Float {
|
||||
NotFloating(Option<(Point<i32, Logical>, Size<i32, Logical>)>),
|
||||
/// An [Option] of a tuple of the previous location and previous size of the window
|
||||
Tiled(Option<(Point<i32, Logical>, Size<i32, Logical>)>),
|
||||
Floating,
|
||||
}
|
||||
|
||||
impl WindowState {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
floating: Float::NotFloating(None), // TODO: get this from a config file instead of
|
||||
// | hardcoding
|
||||
}
|
||||
Default::default()
|
||||
}
|
||||
|
||||
/// Access a [Window]'s state
|
||||
pub fn with_state<F, T>(window: &Window, mut func: F) -> T
|
||||
where
|
||||
F: FnMut(&mut Self) -> T,
|
||||
{
|
||||
window
|
||||
.user_data()
|
||||
.insert_if_missing(RefCell::<Self>::default);
|
||||
|
||||
let mut state = window
|
||||
.user_data()
|
||||
.get::<RefCell<Self>>()
|
||||
.unwrap()
|
||||
.borrow_mut();
|
||||
func(&mut state)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for WindowState {
|
||||
fn default() -> Self {
|
||||
Self::new() // TODO: maybe actual defaults
|
||||
Self {
|
||||
// TODO: get this from a config file instead of hardcoding
|
||||
floating: Float::Tiled(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SurfaceState for WindowState {}
|
||||
|
|
Loading…
Reference in a new issue