diff --git a/src/lib.rs b/src/lib.rs index a50e538..f11935c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -89,6 +89,9 @@ pub struct AdwaitaFrame { title: Option, title_text: Option, shadow: Shadow, + + /// Draw decorations but without the titlebar + hide_titlebar: bool, } impl AdwaitaFrame @@ -111,6 +114,7 @@ where &base_surface, &subcompositor, &queue_handle, + frame_config.hide_titlebar, )); let theme = frame_config.theme; @@ -134,6 +138,7 @@ where wm_capabilities: WindowManagerCapabilities::all(), resizable: true, shadow: Shadow::default(), + hide_titlebar: frame_config.hide_titlebar, }) } @@ -210,6 +215,12 @@ where if self.state.contains(WindowState::FULLSCREEN) { decorations.hide(); return Some(true); + } else { + decorations.show(); + } + + if self.hide_titlebar { + decorations.hide_titlebar(); } let colors = if self.state.contains(WindowState::ACTIVATED) { @@ -228,10 +239,7 @@ where let border_paint = colors.border_paint(); // Draw the borders. - for (idx, part) in decorations - .parts() - .filter(|(idx, _)| *idx == DecorationParts::HEADER || draw_borders) - { + for (idx, part) in decorations.parts().filter(|(_, part)| !part.hide) { let scale = self.scale_factor; let mut rect = part.surface_rect; @@ -329,6 +337,21 @@ where visible_border_size as f32, ) } + // Unless titlebar is disabled + DecorationParts::TOP if self.hide_titlebar => { + let x = rect.x.unsigned_abs() * scale; + let x = x.saturating_sub(visible_border_size); + + let y = rect.y.unsigned_abs() * scale; + let y = y.saturating_sub(visible_border_size); + + Rect::from_xywh( + x as f32, + y as f32, + (rect.width - 2 * x) as f32, + visible_border_size as f32, + ) + } _ => None, }; @@ -407,6 +430,7 @@ where &self.base_surface, &self.subcompositor, &self.queue_handle, + self.hide_titlebar, )); self.dirty = true; self.should_sync = true; @@ -440,7 +464,10 @@ where width: NonZeroU32, height: NonZeroU32, ) -> (Option, Option) { - if self.decorations.is_none() || self.state.contains(WindowState::FULLSCREEN) { + if self.decorations.is_none() + || self.state.contains(WindowState::FULLSCREEN) + || self.hide_titlebar + { (Some(width), Some(height)) } else { ( @@ -451,7 +478,10 @@ where } fn add_borders(&self, width: u32, height: u32) -> (u32, u32) { - if self.decorations.is_none() || self.state.contains(WindowState::FULLSCREEN) { + if self.decorations.is_none() + || self.state.contains(WindowState::FULLSCREEN) + || self.hide_titlebar + { (width, height) } else { (width, height + HEADER_SIZE) @@ -459,7 +489,10 @@ where } fn location(&self) -> (i32, i32) { - if self.decorations.is_none() || self.state.contains(WindowState::FULLSCREEN) { + if self.decorations.is_none() + || self.state.contains(WindowState::FULLSCREEN) + || self.hide_titlebar + { (0, 0) } else { (0, -(HEADER_SIZE as i32)) @@ -545,39 +578,44 @@ where #[derive(Debug, Clone)] pub struct FrameConfig { pub theme: ColorTheme, + /// Draw decorations but without the titlebar + pub hide_titlebar: bool, } impl FrameConfig { /// Create the new configuration with the given `theme`. pub fn new(theme: ColorTheme) -> Self { - Self { theme } + Self { + theme, + hide_titlebar: false, + } } /// This is equivalent of calling `FrameConfig::new(ColorTheme::auto())`. /// /// For details see [`ColorTheme::auto`]. pub fn auto() -> Self { - Self { - theme: ColorTheme::auto(), - } + Self::new(ColorTheme::auto()) } /// This is equivalent of calling `FrameConfig::new(ColorTheme::light())`. /// /// For details see [`ColorTheme::light`]. pub fn light() -> Self { - Self { - theme: ColorTheme::light(), - } + Self::new(ColorTheme::light()) } /// This is equivalent of calling `FrameConfig::new(ColorTheme::dark())`. /// /// For details see [`ColorTheme::dark`]. pub fn dark() -> Self { - Self { - theme: ColorTheme::dark(), - } + Self::new(ColorTheme::dark()) + } + + /// Draw decorations but without the titlebar + pub fn hide_titlebar(mut self, hide: bool) -> Self { + self.hide_titlebar = hide; + self } } diff --git a/src/parts.rs b/src/parts.rs index 7ae54ed..0f2267a 100644 --- a/src/parts.rs +++ b/src/parts.rs @@ -16,6 +16,7 @@ use crate::{pointer::Location, wl_typed::WlTyped}; #[derive(Debug)] pub struct DecorationParts { parts: [Part; 5], + hide_titlebar: bool, } impl DecorationParts { @@ -32,10 +33,13 @@ impl DecorationParts { base_surface: &WlTyped, subcompositor: &SubcompositorState, queue_handle: &QueueHandle, + hide_titlebar: bool, ) -> Self where State: Dispatch + Dispatch + 'static, { + let header_offset = if hide_titlebar { 0 } else { HEADER_SIZE }; + // XXX the order must be in sync with associated constants. let parts = [ // Top. @@ -45,7 +49,7 @@ impl DecorationParts { queue_handle, Rect { x: -(BORDER_SIZE as i32), - y: -(HEADER_SIZE as i32 + BORDER_SIZE as i32), + y: -(header_offset as i32 + BORDER_SIZE as i32), width: 0, // Defined by `Self::resize`. height: BORDER_SIZE, }, @@ -63,7 +67,7 @@ impl DecorationParts { queue_handle, Rect { x: -(BORDER_SIZE as i32), - y: -(HEADER_SIZE as i32), + y: -(header_offset as i32), width: BORDER_SIZE, height: 0, // Defined by `Self::resize`. }, @@ -81,7 +85,7 @@ impl DecorationParts { queue_handle, Rect { x: 0, // Defined by `Self::resize`. - y: -(HEADER_SIZE as i32), + y: -(header_offset as i32), width: BORDER_SIZE, height: 0, // Defined by `Self::resize`. }, @@ -125,32 +129,72 @@ impl DecorationParts { ), ]; - Self { parts } + Self { + parts, + hide_titlebar, + } } pub fn parts(&self) -> std::iter::Enumerate> { self.parts.iter().enumerate() } - pub fn hide(&self) { - for part in self.parts.iter() { + pub fn parts_mut(&mut self) -> std::iter::Enumerate> { + self.parts.iter_mut().enumerate() + } + + pub fn borders_mut(&mut self) -> impl Iterator { + self.parts_mut() + .filter(|(idx, _)| *idx != Self::HEADER) + .map(|(_, p)| p) + } + + pub fn header(&self) -> &Part { + &self.parts[Self::HEADER] + } + + pub fn header_mut(&mut self) -> &mut Part { + &mut self.parts[Self::HEADER] + } + + pub fn hide(&mut self) { + for part in self.parts.iter_mut() { + part.hide = true; part.subsurface.set_sync(); part.surface.attach(None, 0, 0); part.surface.commit(); } } - pub fn hide_borders(&self) { - for (_, part) in self.parts().filter(|(idx, _)| *idx != Self::HEADER) { + pub fn show(&mut self) { + for part in self.parts.iter_mut() { + part.hide = false; + } + } + + pub fn hide_borders(&mut self) { + for part in self.borders_mut() { + part.hide = true; part.surface.attach(None, 0, 0); part.surface.commit(); } } + pub fn hide_titlebar(&mut self) { + let part = self.header_mut(); + part.hide = true; + part.surface.attach(None, 0, 0); + part.surface.commit(); + } + // These unwraps are guaranteed to succeed because the affected options are filled above // and then never emptied afterwards. #[allow(clippy::unwrap_used)] pub fn resize(&mut self, width: u32, height: u32) { + let header_size = if self.hide_titlebar { 0 } else { HEADER_SIZE }; + + let height_with_header = height + header_size; + self.parts[Self::HEADER].surface_rect.width = width; self.parts[Self::BOTTOM].surface_rect.width = width + 2 * BORDER_SIZE; @@ -163,18 +207,12 @@ impl DecorationParts { self.parts[Self::TOP].input_rect.as_mut().unwrap().width = self.parts[Self::TOP].surface_rect.width - (BORDER_SIZE * 2) + (RESIZE_HANDLE_SIZE * 2); - self.parts[Self::LEFT].surface_rect.height = height + HEADER_SIZE; - self.parts[Self::LEFT].input_rect.as_mut().unwrap().height = - self.parts[Self::LEFT].surface_rect.height; + self.parts[Self::LEFT].surface_rect.height = height_with_header; + self.parts[Self::LEFT].input_rect.as_mut().unwrap().height = height_with_header; - self.parts[Self::RIGHT].surface_rect.height = self.parts[Self::LEFT].surface_rect.height; + self.parts[Self::RIGHT].surface_rect.height = height_with_header; self.parts[Self::RIGHT].surface_rect.x = width as i32; - self.parts[Self::RIGHT].input_rect.as_mut().unwrap().height = - self.parts[Self::RIGHT].surface_rect.height; - } - - pub fn header(&self) -> &Part { - &self.parts[Self::HEADER] + self.parts[Self::RIGHT].input_rect.as_mut().unwrap().height = height_with_header; } pub fn side_height(&self) -> u32 { @@ -221,6 +259,8 @@ pub struct Part { /// /// `None` if it fully covers `surface_rect`. pub input_rect: Option, + + pub hide: bool, } impl Part { @@ -248,6 +288,7 @@ impl Part { subsurface, surface_rect, input_rect, + hide: false, } } }