Skip to content

Commit

Permalink
Add TitleBar widget.
Browse files Browse the repository at this point in the history
  • Loading branch information
melix99 committed Oct 2, 2024
1 parent 09a927d commit 362a216
Show file tree
Hide file tree
Showing 5 changed files with 310 additions and 0 deletions.
2 changes: 2 additions & 0 deletions masonry/src/theme.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ use crate::Insets;
// They're picked for visual distinction and accessibility (99 percent)

pub const WINDOW_BACKGROUND_COLOR: Color = Color::rgb8(0x29, 0x29, 0x29);
pub const TITLE_BAR_HEIGHT: f64 = 32.0;
pub const TITLE_BAR_COLOR: Color = Color::rgb8(0x1a, 0x1a, 0x1a);
pub const TEXT_COLOR: Color = Color::rgb8(0xf0, 0xf0, 0xea);
pub const DISABLED_TEXT_COLOR: Color = Color::rgb8(0xa0, 0xa0, 0x9a);
pub const PLACEHOLDER_COLOR: Color = Color::rgb8(0x80, 0x80, 0x80);
Expand Down
113 changes: 113 additions & 0 deletions masonry/src/widget/center_box.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// Copyright 2024 the Xilem Authors and the Druid Authors
// SPDX-License-Identifier: Apache-2.0

//! A center box widget.
use accesskit::{NodeBuilder, Role};
use smallvec::SmallVec;
use tracing::{trace_span, Span};
use vello::kurbo::Point;
use vello::Scene;

use crate::widget::WidgetPod;
use crate::{
AccessCtx, AccessEvent, BoxConstraints, EventCtx, LayoutCtx, LifeCycleCtx, PaintCtx,
PointerEvent, Size, StatusChange, TextEvent, Widget, WidgetId,
};

const PADDING: f64 = 10.;

/// A center box widget.
pub struct CenterBox {
child: Option<WidgetPod<Box<dyn Widget>>>,
end: Option<WidgetPod<Box<dyn Widget>>>,
}

// --- MARK: BUILDERS ---
impl CenterBox {
// TODO: Maybe the end widget should be optional here
pub(crate) fn new(child: impl Widget, end: impl Widget) -> Self {
Self {
child: Some(WidgetPod::new(child).boxed()),
end: Some(WidgetPod::new(end).boxed()),
}
}
}

// --- MARK: IMPL WIDGET ---
impl Widget for CenterBox {
fn on_pointer_event(&mut self, _ctx: &mut EventCtx, _event: &PointerEvent) {}

fn on_text_event(&mut self, _ctx: &mut EventCtx, _event: &TextEvent) {}

fn on_access_event(&mut self, _ctx: &mut EventCtx, _event: &AccessEvent) {}

fn on_status_change(&mut self, _ctx: &mut LifeCycleCtx, _event: &StatusChange) {}

fn register_children(&mut self, ctx: &mut crate::RegisterCtx) {
if let Some(ref mut child) = self.child {
ctx.register_child(child);
}

if let Some(ref mut end) = self.end {
ctx.register_child(end);
}
}

fn layout(&mut self, ctx: &mut LayoutCtx, bc: &BoxConstraints) -> Size {
let child = self.child.as_mut().unwrap();
let end = self.end.as_mut().unwrap();

let child_size = ctx.run_layout(child, &bc.loosen());
let end_size = ctx.run_layout(end, &bc.loosen());

let box_size = bc.constrain(Size::new(
child_size.width + end_size.width,
child_size.height.max(end_size.height),
));

ctx.place_child(
child,
Point::new(
(box_size.width / 2.0) - (child_size.width / 2.0),
(box_size.height / 2.0) - (child_size.height / 2.0),
),
);

ctx.place_child(
end,
Point::new(
box_size.width - end_size.width - PADDING,
(box_size.height / 2.0) - (end_size.height / 2.0),
),
);

box_size
}

fn paint(&mut self, _ctx: &mut PaintCtx, _scene: &mut Scene) {}

fn accessibility_role(&self) -> Role {
Role::GenericContainer
}

fn accessibility(&mut self, _ctx: &mut AccessCtx, _node: &mut NodeBuilder) {}

fn children_ids(&self) -> SmallVec<[WidgetId; 16]> {
let mut vec = SmallVec::new();

if let Some(child) = &self.child {
vec.push(child.id());
}

if let Some(end) = &self.end {
vec.push(end.id());
}

vec
}

fn make_trace_span(&self) -> Span {
trace_span!("CenterBox")
}
}
7 changes: 7 additions & 0 deletions masonry/src/widget/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ mod tests;

mod align;
mod button;
mod center_box;
mod checkbox;
mod flex;
mod grid;
Expand All @@ -29,8 +30,10 @@ mod sized_box;
mod spinner;
mod split;
mod textbox;
mod title_bar;
mod variable_label;
mod widget_arena;
mod window_button;
mod window_handle;

pub use self::image::Image;
Expand All @@ -49,6 +52,7 @@ pub use sized_box::SizedBox;
pub use spinner::Spinner;
pub use split::Split;
pub use textbox::Textbox;
pub use title_bar::TitleBar;
pub use variable_label::VariableLabel;
pub use widget_mut::WidgetMut;
pub use widget_pod::WidgetPod;
Expand All @@ -58,6 +62,9 @@ pub use window_handle::WindowHandle;

pub(crate) use widget_arena::WidgetArena;

use center_box::CenterBox;
use window_button::{WindowButton, WindowButtonType};

use crate::{Affine, Size};

// These are based on https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit
Expand Down
89 changes: 89 additions & 0 deletions masonry/src/widget/title_bar.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Copyright 2024 the Xilem Authors and the Druid Authors
// SPDX-License-Identifier: Apache-2.0

//! A titlebar widget.
use accesskit::{NodeBuilder, Role};
use smallvec::{smallvec, SmallVec};
use tracing::{trace_span, Span};
use vello::kurbo::Point;
use vello::Scene;

use crate::paint_scene_helpers::fill_color;
use crate::widget::WidgetPod;
use crate::{
theme, AccessCtx, AccessEvent, BoxConstraints, EventCtx, LayoutCtx, LifeCycleCtx, PaintCtx,
PointerEvent, Size, StatusChange, TextEvent, Widget, WidgetId,
};

use super::{CenterBox, Flex, Label, WindowButton, WindowButtonType, WindowHandle};

/// A titlebar widget.
pub struct TitleBar {
child: WidgetPod<WindowHandle>,
}

// --- MARK: BUILDERS ---
impl TitleBar {
pub fn new() -> Self {
let title = CenterBox::new(
// TODO: Get the title from the window
Label::new("Title"),
Flex::row()
.with_child(WindowButton::new(WindowButtonType::Minimize))
.with_child(WindowButton::new(WindowButtonType::Maximize))
.with_child(WindowButton::new(WindowButtonType::Close)),
);

let handle = WindowHandle::new(title);

Self {
child: WidgetPod::new(handle),
}
}
}

// --- MARK: IMPL WIDGET ---
impl Widget for TitleBar {
fn on_pointer_event(&mut self, _ctx: &mut EventCtx, _event: &PointerEvent) {}

fn on_text_event(&mut self, _ctx: &mut EventCtx, _event: &TextEvent) {}

fn on_access_event(&mut self, _ctx: &mut EventCtx, _event: &AccessEvent) {}

fn on_status_change(&mut self, _ctx: &mut LifeCycleCtx, _event: &StatusChange) {}

fn register_children(&mut self, ctx: &mut crate::RegisterCtx) {
ctx.register_child(&mut self.child);
}

fn layout(&mut self, ctx: &mut LayoutCtx, bc: &BoxConstraints) -> Size {
let child_size = ctx.run_layout(&mut self.child, bc);
let size = bc.constrain((
child_size.width,
child_size.height.max(theme::TITLE_BAR_HEIGHT),
));

ctx.place_child(&mut self.child, Point::ORIGIN);
size
}

fn paint(&mut self, ctx: &mut PaintCtx, scene: &mut Scene) {
let bounds = ctx.size().to_rect();
fill_color(scene, &bounds, theme::TITLE_BAR_COLOR);
}

fn accessibility_role(&self) -> Role {
Role::TitleBar
}

fn accessibility(&mut self, _ctx: &mut AccessCtx, _node: &mut NodeBuilder) {}

fn children_ids(&self) -> SmallVec<[WidgetId; 16]> {
smallvec![self.child.id()]
}

fn make_trace_span(&self) -> Span {
trace_span!("TitleBar")
}
}
99 changes: 99 additions & 0 deletions masonry/src/widget/window_button.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Copyright 2024 the Xilem Authors and the Druid Authors
// SPDX-License-Identifier: Apache-2.0

//! A window button.
use accesskit::{NodeBuilder, Role};
use smallvec::{smallvec, SmallVec};
use tracing::{trace_span, Span};
use vello::kurbo::Point;
use vello::Scene;

use crate::widget::WidgetPod;
use crate::{
AccessCtx, AccessEvent, BoxConstraints, EventCtx, LayoutCtx, LifeCycleCtx, PaintCtx,
PointerEvent, Size, StatusChange, TextEvent, Widget, WidgetId,
};

use super::Label;

pub enum WindowButtonType {
Close,
Maximize,
Minimize,
}

/// A window button.
pub struct WindowButton {
child: WidgetPod<Label>,
type_: WindowButtonType,
}

// --- MARK: BUILDERS ---
impl WindowButton {
pub fn new(type_: WindowButtonType) -> Self {
let handle = Label::new(match type_ {
WindowButtonType::Close => "×",
WindowButtonType::Maximize => "+",
WindowButtonType::Minimize => "−",
})
.with_text_size(20.);

Self {
child: WidgetPod::new(handle),
type_,
}
}
}

// --- MARK: IMPL WIDGET ---
impl Widget for WindowButton {
fn on_pointer_event(&mut self, ctx: &mut EventCtx, event: &PointerEvent) {
match event {
PointerEvent::PointerDown(_, _) => {
if !ctx.is_disabled() {
ctx.capture_pointer();
}
}
PointerEvent::PointerUp(_, _) => match self.type_ {
WindowButtonType::Close => ctx.exit(),
WindowButtonType::Maximize => ctx.toggle_maximized(),
WindowButtonType::Minimize => ctx.minimize(),
},
PointerEvent::PointerLeave(_) => {}
_ => (),
}
}

fn on_text_event(&mut self, _ctx: &mut EventCtx, _event: &TextEvent) {}

fn on_access_event(&mut self, _ctx: &mut EventCtx, _event: &AccessEvent) {}

fn on_status_change(&mut self, _ctx: &mut LifeCycleCtx, _event: &StatusChange) {}

fn register_children(&mut self, ctx: &mut crate::RegisterCtx) {
ctx.register_child(&mut self.child);
}

fn layout(&mut self, ctx: &mut LayoutCtx, bc: &BoxConstraints) -> Size {
let size = ctx.run_layout(&mut self.child, bc);
ctx.place_child(&mut self.child, Point::ORIGIN);
size
}

fn paint(&mut self, _ctx: &mut PaintCtx, _scene: &mut Scene) {}

fn accessibility_role(&self) -> Role {
Role::Button
}

fn accessibility(&mut self, _ctx: &mut AccessCtx, _node: &mut NodeBuilder) {}

fn children_ids(&self) -> SmallVec<[WidgetId; 16]> {
smallvec![self.child.id()]
}

fn make_trace_span(&self) -> Span {
trace_span!("WindowButton")
}
}

0 comments on commit 362a216

Please sign in to comment.