makepad/widgets/src/desktop_button.rs
Kevin Boos f99b1d875a Fix title bar, captions label centering, and windows buttons (#999)
no app-level overrides are needed now.

1. on Windows, the windows buttons now behave and are drawn
   just like all other apps -- proper bg coloring on hover and down,
   and the right sizing.

2. and for the caption label, it is centered properly (by accounting
   for the size of the windows_buttons button set), and then when the
   window is too narrow, it is left-aligned in the remaining space
   to ensure that it stil looks good.
2026-03-31 08:58:06 +02:00

250 lines
8.5 KiB
Rust

use crate::{
animator::{Animator, AnimatorAction, AnimatorImpl, Play},
button::ButtonAction,
makepad_derive_widget::*,
makepad_draw::*,
widget::*,
};
script_mod! {
use mod.prelude.widgets_internal.*
use mod.widgets.*
let DesktopButtonType = set_type_default() do #(DesktopButtonType::script_api(vm))
mod.widgets.DesktopButtonType = DesktopButtonType
mod.widgets.DesktopButtonBase = #(DesktopButton::register_widget(vm))
mod.widgets.DesktopButton = set_type_default() do mod.widgets.DesktopButtonBase{
width: 46 height: 29
draw_bg +: {
button_type: instance(DesktopButtonType.Fullscreen)
hover: instance(0.0)
down: instance(0.0)
color: uniform(theme.color_label_inner)
color_hover: uniform(theme.color_label_inner_hover)
color_down: uniform(theme.color_label_inner_down)
bg_color: uniform(#00000000)
bg_color_hover: uniform(#00000000)
bg_color_down: uniform(#00000000)
pixel: fn() {
let sdf = Sdf2d.viewport(self.pos * self.rect_size)
sdf.aa = sdf.aa * 3.0
let sz = 4.5
let c = self.rect_size * vec2(0.5, 0.5)
// Draw background
let bg = self.bg_color
.mix(self.bg_color_hover, self.hover)
.mix(self.bg_color_down, self.down);
sdf.rect(0., 0., self.rect_size.x, self.rect_size.y);
sdf.fill(bg);
let color = self.color
.mix(self.color_hover, self.hover)
.mix(self.color_down, self.down)
match self.button_type {
DesktopButtonType.WindowsMin => {
sdf.move_to(c.x - sz, c.y)
sdf.line_to(c.x + sz, c.y)
sdf.stroke(color, 0.5 + 0.5 * self.draw_pass.dpi_dilate)
return sdf.result
}
DesktopButtonType.WindowsMax => {
sdf.rect(c.x - sz, c.y - sz, 2. * sz, 2. * sz)
sdf.stroke(color, 0.5 + 0.5 * self.draw_pass.dpi_dilate)
return sdf.result
}
DesktopButtonType.WindowsMaxToggled => {
let sz = 5.
sdf.rect(c.x - sz + 1., c.y - sz - 1., 2. * sz, 2. * sz)
sdf.stroke(#f, 0.5 + 0.5 * self.draw_pass.dpi_dilate)
sdf.rect(c.x - sz - 1., c.y - sz + 1., 2. * sz, 2. * sz)
sdf.stroke(color, 0.5 + 0.5 * self.draw_pass.dpi_dilate)
return sdf.result
}
DesktopButtonType.WindowsClose => {
sdf.move_to(c.x - sz, c.y - sz)
sdf.line_to(c.x + sz, c.y + sz)
sdf.move_to(c.x - sz, c.y + sz)
sdf.line_to(c.x + sz, c.y - sz)
sdf.stroke(color, 0.5 + 0.5 * self.draw_pass.dpi_dilate)
return sdf.result
}
DesktopButtonType.XRMode => {
let w = 12.
let h = 8.
sdf.box(c.x - w, c.y - h, 2. * w, 2. * h, 2.)
sdf.circle(c.x - 5.5, c.y, 3.5)
sdf.subtract()
sdf.circle(c.x + 5.5, c.y, 3.5)
sdf.subtract()
sdf.circle(c.x, c.y + h - 0.75, 2.5)
sdf.subtract()
sdf.fill(color)
return sdf.result
}
DesktopButtonType.Fullscreen => {
let sz = 8.
sdf.rect(c.x - sz, c.y - sz, 2. * sz, 2. * sz)
sdf.rect(c.x - sz + 1.5, c.y - sz + 1.5, 2. * (sz - 1.5), 2. * (sz - 1.5))
sdf.subtract()
sdf.rect(c.x - sz + 4., c.y - sz - 2., 2. * (sz - 4.), 2. * (sz + 2.))
sdf.subtract()
sdf.rect(c.x - sz - 2., c.y - sz + 4., 2. * (sz + 2.), 2. * (sz - 4.))
sdf.subtract()
sdf.fill(color)
return sdf.result
}
DesktopButtonType.RecordOff => {
let rr = 5.0
sdf.circle(c.x, c.y, rr)
sdf.stroke(#ff3b30, 1.0 + 0.5 * self.draw_pass.dpi_dilate)
return sdf.result
}
DesktopButtonType.RecordOn => {
let rr = 5.0
sdf.circle(c.x, c.y, rr)
sdf.fill(#ff3b30)
return sdf.result
}
}
return #f00
}
}
animator: Animator{
hover: {
default: @off
off: AnimatorState{
from: {all: Forward {duration: 0.1}}
apply: {
draw_bg: {down: 0.0, hover: 0.0}
}
}
on: AnimatorState{
from: {
all: Forward {duration: 0.1}
down: Snap
}
apply: {
draw_bg: {
down: 0.0
hover: 1.0
}
}
}
down: AnimatorState{
from: {all: Snap}
apply: {
draw_bg: {
down: 1.0
hover: 1.0
}
}
}
}
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Script, ScriptHook)]
#[repr(u32)]
pub enum DesktopButtonType {
WindowsMin = 1,
WindowsMax = 2,
WindowsMaxToggled = 3,
WindowsClose = 4,
XRMode = 5,
#[pick]
Fullscreen = 6,
RecordOff = 7,
RecordOn = 8,
}
#[derive(Script, ScriptHook, Widget, Animator)]
pub struct DesktopButton {
#[uid]
uid: WidgetUid,
#[source]
source: ScriptObjectRef,
#[apply_default]
animator: Animator,
#[walk]
walk: Walk,
#[visible]
#[live(true)]
visible: bool,
#[redraw]
#[live]
draw_bg: DrawQuad,
}
impl Widget for DesktopButton {
fn handle_event(&mut self, cx: &mut Cx, event: &Event, _scope: &mut Scope) {
if !self.visible {
return;
}
let uid = self.widget_uid();
if self.animator_handle_event(cx, event).must_redraw() {
self.draw_bg.redraw(cx);
}
match event.hits(cx, self.draw_bg.area()) {
Hit::FingerDown(fe) => {
cx.widget_action(uid, ButtonAction::Pressed(fe.modifiers));
self.animator_play(cx, ids!(hover.down));
}
Hit::FingerHoverIn(_) => {
cx.set_cursor(MouseCursor::Hand);
self.animator_play(cx, ids!(hover.on));
}
Hit::FingerHoverOut(_) => {
self.animator_play(cx, ids!(hover.off));
}
Hit::FingerLongPress(_) => {
cx.widget_action(uid, ButtonAction::LongPressed);
}
Hit::FingerUp(fe) => {
if fe.is_over {
cx.widget_action(uid, ButtonAction::Clicked(fe.modifiers));
if fe.device.has_hovers() {
self.animator_play(cx, ids!(hover.on));
} else {
self.animator_play(cx, ids!(hover.off));
}
} else {
cx.widget_action(uid, ButtonAction::Released(fe.modifiers));
self.animator_play(cx, ids!(hover.off));
}
}
_ => (),
};
}
fn draw_walk(&mut self, cx: &mut Cx2d, _scope: &mut Scope, walk: Walk) -> DrawStep {
if !self.visible {
return DrawStep::done();
}
let _ = self.draw_bg.draw_walk(cx, walk);
DrawStep::done()
}
}
impl DesktopButtonRef {
pub fn clicked(&self, actions: &Actions) -> bool {
if let Some(item) = actions.find_widget_action(self.widget_uid()) {
matches!(item.cast(), ButtonAction::Clicked(_))
} else {
false
}
}
}