Simple floating window support

This commit is contained in:
Seaotatop 2023-06-18 19:30:52 -05:00
parent 3ea2452397
commit 71f71feaf2
10 changed files with 98 additions and 60 deletions

View file

@ -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

View file

@ -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({

View file

@ -14,6 +14,9 @@ pub enum Msg {
CloseWindow {
client_id: Option<u32>,
},
ToggleFloating {
client_id: Option<u32>,
},
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]

View file

@ -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,
);
});
}
}

View file

@ -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);

View file

@ -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();

View file

@ -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;

View file

@ -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!(),

View file

@ -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);
}

View file

@ -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 {}