mirror of
https://github.com/wez/wezterm.git
synced 2024-12-23 21:32:13 +03:00
windows: implement decoration setting, change to bitfield
Can now set `window_decorations = "TITLE|RESIZE"` or variations on those flags. `NONE` is a shortcut for no flags.
This commit is contained in:
parent
945b6b726f
commit
1178639a22
@ -25,7 +25,7 @@ use std::time::Duration;
|
||||
use termwiz::hyperlink;
|
||||
use termwiz::surface::CursorShape;
|
||||
use toml;
|
||||
use wezterm_input_types::{KeyCode, Modifiers};
|
||||
use wezterm_input_types::{KeyCode, Modifiers, WindowDecorations};
|
||||
|
||||
mod color;
|
||||
mod daemon;
|
||||
@ -1160,18 +1160,6 @@ pub struct LoadedConfig {
|
||||
lua: Option<mlua::Lua>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub enum WindowDecorations {
|
||||
Full,
|
||||
None,
|
||||
}
|
||||
|
||||
impl Default for WindowDecorations {
|
||||
fn default() -> Self {
|
||||
Self::Full
|
||||
}
|
||||
}
|
||||
|
||||
struct PathPossibility {
|
||||
path: PathBuf,
|
||||
is_required: bool,
|
||||
|
@ -50,11 +50,6 @@ impl WindowConfiguration for ConfigBridge {
|
||||
}
|
||||
|
||||
fn decorations(&self) -> ::window::WindowDecorations {
|
||||
use ::config::WindowDecorations as CWD;
|
||||
use ::window::WindowDecorations as WD;
|
||||
match configuration().window_decorations {
|
||||
CWD::Full => WD::Full,
|
||||
CWD::None => WD::None,
|
||||
}
|
||||
configuration().window_decorations
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
use bitflags::*;
|
||||
use serde::*;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
pub struct PixelUnit;
|
||||
pub struct ScreenPixelUnit;
|
||||
@ -253,3 +254,39 @@ impl KeyEvent {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[serde(try_from = "String")]
|
||||
pub struct WindowDecorations: u8 {
|
||||
const TITLE = 1;
|
||||
const RESIZE = 2;
|
||||
const NONE = 0;
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for WindowDecorations {
|
||||
type Error = String;
|
||||
fn try_from(s: String) -> std::result::Result<WindowDecorations, String> {
|
||||
let mut flags = Self::NONE;
|
||||
for ele in s.split('|') {
|
||||
let ele = ele.trim();
|
||||
if ele == "TITLE" {
|
||||
flags |= Self::TITLE;
|
||||
} else if ele == "NONE" || ele == "None" {
|
||||
flags = Self::NONE;
|
||||
} else if ele == "RESIZE" {
|
||||
flags |= Self::RESIZE;
|
||||
} else {
|
||||
return Err(format!("invalid WindowDecoration name {} in {}", ele, s));
|
||||
}
|
||||
}
|
||||
Ok(flags)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for WindowDecorations {
|
||||
fn default() -> Self {
|
||||
WindowDecorations::TITLE | WindowDecorations::RESIZE
|
||||
}
|
||||
}
|
||||
|
@ -78,18 +78,6 @@ pub enum MouseCursor {
|
||||
SizeLeftRight,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum WindowDecorations {
|
||||
Full,
|
||||
None,
|
||||
}
|
||||
|
||||
impl Default for WindowDecorations {
|
||||
fn default() -> Self {
|
||||
Self::Full
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
pub trait WindowCallbacks: Any {
|
||||
/// Called when the window close button is clicked.
|
||||
|
@ -859,14 +859,24 @@ impl WindowOpsMut for WindowInner {
|
||||
}
|
||||
|
||||
fn decoration_to_mask(decorations: WindowDecorations) -> NSWindowStyleMask {
|
||||
match decorations {
|
||||
WindowDecorations::Full => {
|
||||
NSWindowStyleMask::NSTitledWindowMask
|
||||
| NSWindowStyleMask::NSClosableWindowMask
|
||||
| NSWindowStyleMask::NSMiniaturizableWindowMask
|
||||
| NSWindowStyleMask::NSResizableWindowMask
|
||||
}
|
||||
WindowDecorations::None => NSWindowStyleMask::NSResizableWindowMask,
|
||||
if decorations == WindowDecorations::TITLE | WindowDecorations::RESIZE {
|
||||
NSWindowStyleMask::NSTitledWindowMask
|
||||
| NSWindowStyleMask::NSClosableWindowMask
|
||||
| NSWindowStyleMask::NSMiniaturizableWindowMask
|
||||
| NSWindowStyleMask::NSResizableWindowMask
|
||||
} else if decorations == WindowDecorations::RESIZE {
|
||||
NSWindowStyleMask::NSResizableWindowMask
|
||||
} else if decorations == WindowDecorations::NONE {
|
||||
NSWindowStyleMask::NSBorderlessWindowMask
|
||||
} else if decorations == WindowDecorations::TITLE {
|
||||
NSWindowStyleMask::NSTitledWindowMask
|
||||
| NSWindowStyleMask::NSClosableWindowMask
|
||||
| NSWindowStyleMask::NSMiniaturizableWindowMask
|
||||
} else {
|
||||
NSWindowStyleMask::NSTitledWindowMask
|
||||
| NSWindowStyleMask::NSClosableWindowMask
|
||||
| NSWindowStyleMask::NSMiniaturizableWindowMask
|
||||
| NSWindowStyleMask::NSResizableWindowMask
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,14 +64,14 @@ fn rect_height(r: &RECT) -> i32 {
|
||||
r.bottom - r.top
|
||||
}
|
||||
|
||||
fn adjust_client_to_window_dimensions(width: usize, height: usize) -> (i32, i32) {
|
||||
fn adjust_client_to_window_dimensions(style: u32, width: usize, height: usize) -> (i32, i32) {
|
||||
let mut rect = RECT {
|
||||
left: 0,
|
||||
top: 0,
|
||||
right: width as _,
|
||||
bottom: height as _,
|
||||
};
|
||||
unsafe { AdjustWindowRect(&mut rect, WS_POPUP | WS_SYSMENU | WS_CAPTION, 0) };
|
||||
unsafe { AdjustWindowRect(&mut rect, style, 0) };
|
||||
|
||||
(rect_width(&rect), rect_height(&rect))
|
||||
}
|
||||
@ -215,34 +215,46 @@ impl WindowInner {
|
||||
|
||||
fn apply_decoration(&mut self) {
|
||||
let hwnd = self.hwnd.0;
|
||||
promise::spawn::spawn(async move {
|
||||
unsafe {
|
||||
let orig_style = GetWindowLongW(hwnd, GWL_STYLE);
|
||||
let style = decorations_to_style(config().decorations());
|
||||
SetWindowLongW(
|
||||
hwnd,
|
||||
GWL_STYLE,
|
||||
(orig_style & !(WS_OVERLAPPEDWINDOW as i32)) | style as i32,
|
||||
);
|
||||
SetWindowPos(
|
||||
hwnd,
|
||||
std::ptr::null_mut(),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_FRAMECHANGED,
|
||||
);
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
schedule_apply_decoration(hwnd, config().decorations());
|
||||
}
|
||||
}
|
||||
|
||||
fn schedule_apply_decoration(hwnd: HWND, decorations: WindowDecorations) {
|
||||
promise::spawn::spawn(async move {
|
||||
apply_decoration_immediate(hwnd, decorations);
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
fn apply_decoration_immediate(hwnd: HWND, decorations: WindowDecorations) {
|
||||
unsafe {
|
||||
let orig_style = GetWindowLongW(hwnd, GWL_STYLE);
|
||||
let style = decorations_to_style(decorations);
|
||||
let new_style = (orig_style & !(WS_OVERLAPPEDWINDOW as i32)) | style as i32;
|
||||
SetWindowLongW(hwnd, GWL_STYLE, new_style);
|
||||
SetWindowPos(
|
||||
hwnd,
|
||||
std::ptr::null_mut(),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_FRAMECHANGED,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn decorations_to_style(decorations: WindowDecorations) -> u32 {
|
||||
match decorations {
|
||||
WindowDecorations::Full => WS_OVERLAPPEDWINDOW,
|
||||
WindowDecorations::None => WS_THICKFRAME,
|
||||
if decorations == WindowDecorations::RESIZE {
|
||||
WS_THICKFRAME
|
||||
} else if decorations == WindowDecorations::TITLE {
|
||||
WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX
|
||||
} else if decorations == WindowDecorations::NONE {
|
||||
WS_POPUP
|
||||
} else if decorations == WindowDecorations::TITLE | WindowDecorations::RESIZE {
|
||||
WS_OVERLAPPEDWINDOW
|
||||
} else {
|
||||
WS_OVERLAPPEDWINDOW
|
||||
}
|
||||
}
|
||||
|
||||
@ -291,9 +303,33 @@ impl Window {
|
||||
}
|
||||
}
|
||||
|
||||
let (width, height) = adjust_client_to_window_dimensions(width, height);
|
||||
let decorations = config().decorations();
|
||||
let style = decorations_to_style(decorations);
|
||||
let (width, height) = adjust_client_to_window_dimensions(style, width, height);
|
||||
|
||||
let style = decorations_to_style(config().decorations());
|
||||
let (x, y) = if (style & WS_POPUP) == 0 {
|
||||
(CW_USEDEFAULT, CW_USEDEFAULT)
|
||||
} else {
|
||||
// WS_POPUP windows need to specify the initial position.
|
||||
// We pick the middle of the primary monitor
|
||||
|
||||
unsafe {
|
||||
let mut mi: MONITORINFO = std::mem::zeroed();
|
||||
mi.cbSize = std::mem::size_of::<MONITORINFO>() as u32;
|
||||
GetMonitorInfoW(
|
||||
MonitorFromWindow(std::ptr::null_mut(), MONITOR_DEFAULTTOPRIMARY),
|
||||
&mut mi,
|
||||
);
|
||||
|
||||
let mon_width = mi.rcMonitor.right - mi.rcMonitor.left;
|
||||
let mon_height = mi.rcMonitor.bottom - mi.rcMonitor.top;
|
||||
|
||||
(
|
||||
mi.rcMonitor.left + (mon_width - width) / 2,
|
||||
mi.rcMonitor.top + (mon_height - height) / 2,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let name = wide_string(name);
|
||||
let hwnd = unsafe {
|
||||
@ -302,8 +338,8 @@ impl Window {
|
||||
class_name.as_ptr(),
|
||||
name.as_ptr(),
|
||||
style,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
null_mut(),
|
||||
@ -318,6 +354,10 @@ impl Window {
|
||||
bail!("CreateWindowExW: {}", err);
|
||||
}
|
||||
|
||||
// We have to re-apply the styles otherwise they don't
|
||||
// completely stick
|
||||
schedule_apply_decoration(hwnd, decorations);
|
||||
|
||||
Ok(hwnd)
|
||||
}
|
||||
|
||||
@ -410,7 +450,11 @@ impl WindowOpsMut for WindowInner {
|
||||
}
|
||||
|
||||
fn set_inner_size(&mut self, width: usize, height: usize) {
|
||||
let (width, height) = adjust_client_to_window_dimensions(width, height);
|
||||
let (width, height) = adjust_client_to_window_dimensions(
|
||||
decorations_to_style(config().decorations()),
|
||||
width,
|
||||
height,
|
||||
);
|
||||
let hwnd = self.hwnd;
|
||||
promise::spawn::spawn(async move {
|
||||
unsafe {
|
||||
|
@ -611,13 +611,13 @@ impl XWindowInner {
|
||||
xcb::ClientMessageData::from_data32(data),
|
||||
),
|
||||
);
|
||||
self.adjust_decorations(config().decorations() == WindowDecorations::Full)?;
|
||||
self.adjust_decorations(config().decorations())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::identity_op)]
|
||||
fn adjust_decorations(&mut self, enable: bool) -> anyhow::Result<()> {
|
||||
fn adjust_decorations(&mut self, decorations: WindowDecorations) -> anyhow::Result<()> {
|
||||
// Set the motif hints to disable decorations.
|
||||
// See https://stackoverflow.com/a/1909708
|
||||
#[repr(C)]
|
||||
@ -632,18 +632,28 @@ impl XWindowInner {
|
||||
const HINTS_DECORATIONS: u32 = 1 << 1;
|
||||
const FUNC_ALL: u32 = 1 << 0;
|
||||
const FUNC_RESIZE: u32 = 1 << 1;
|
||||
/*
|
||||
const HINTS_FUNCTIONS: u32 = 1 << 0;
|
||||
// const HINTS_FUNCTIONS: u32 = 1 << 0;
|
||||
const FUNC_MOVE: u32 = 1 << 2;
|
||||
const FUNC_MINIMIZE: u32 = 1 << 3;
|
||||
const FUNC_MAXIMIZE: u32 = 1 << 4;
|
||||
const FUNC_CLOSE: u32 = 1 << 5;
|
||||
*/
|
||||
|
||||
let decorations = if decorations == WindowDecorations::TITLE | WindowDecorations::RESIZE {
|
||||
FUNC_ALL
|
||||
} else if decorations == WindowDecorations::RESIZE {
|
||||
FUNC_RESIZE
|
||||
} else if decorations == WindowDecorations::TITLE {
|
||||
FUNC_MOVE | FUNC_MINIMIZE | FUNC_MAXIMIZE | FUNC_CLOSE
|
||||
} else if decorations == WindowDecorations::NONE {
|
||||
0
|
||||
} else {
|
||||
FUNC_ALL
|
||||
};
|
||||
|
||||
let hints = MwmHints {
|
||||
flags: HINTS_DECORATIONS,
|
||||
functions: 0,
|
||||
decorations: if enable { FUNC_ALL } else { FUNC_RESIZE },
|
||||
decorations,
|
||||
input_mode: 0,
|
||||
status: 0,
|
||||
};
|
||||
@ -792,7 +802,7 @@ impl XWindow {
|
||||
window
|
||||
.lock()
|
||||
.unwrap()
|
||||
.adjust_decorations(config().decorations() == WindowDecorations::Full)?;
|
||||
.adjust_decorations(config().decorations())?;
|
||||
|
||||
let window_handle = Window::X11(XWindow::from_id(window_id));
|
||||
|
||||
@ -840,7 +850,7 @@ impl WindowOpsMut for XWindowInner {
|
||||
}
|
||||
|
||||
fn config_did_change(&mut self) {
|
||||
let _ = self.adjust_decorations(config().decorations() == WindowDecorations::Full);
|
||||
let _ = self.adjust_decorations(config().decorations());
|
||||
}
|
||||
|
||||
fn set_inner_size(&mut self, width: usize, height: usize) {
|
||||
|
Loading…
Reference in New Issue
Block a user