add theme titlebar height and app-owned strip dragging
This commit is contained in:
+22
-12
@@ -7,9 +7,9 @@ use std::rc::Rc;
|
||||
use gpui::prelude::FluentBuilder as _;
|
||||
use gpui::{
|
||||
AnyElement, AnyView, App, AppContext as _, Axis, Context, ElementId, Entity,
|
||||
InteractiveElement as _, IntoElement, ParentElement as _, Render, RenderOnce, Role,
|
||||
SharedString, StatefulInteractiveElement as _, StyleRefinement, Styled as _, Window,
|
||||
WindowControlArea, canvas, div, px, relative, size,
|
||||
InteractiveElement as _, IntoElement, ParentElement as _, Pixels, Render, RenderOnce, Role,
|
||||
SharedString, StatefulInteractiveElement as _, StyleRefinement, Styled as _, Window, canvas,
|
||||
div, px, relative, size,
|
||||
};
|
||||
use gpui_native_core::common::{h_flex, v_flex};
|
||||
use gpui_native_core::context_menu::ContextMenu;
|
||||
@@ -19,7 +19,7 @@ use gpui_native_icons::IconRole;
|
||||
use gpui_native_theme::{ActiveTheme as _, Size};
|
||||
|
||||
use crate::resize_handle::ResizeHandle;
|
||||
use crate::window_controls::{TRAFFIC_LIGHT_PAD, WindowControls, maximize_on_double_click};
|
||||
use crate::window_controls::{TRAFFIC_LIGHT_PAD, WindowControls, titlebar_strip};
|
||||
|
||||
/// The narrowest either side of a split can get, as a fraction of the split.
|
||||
const MIN_SPLIT_RATIO: f32 = 0.1;
|
||||
@@ -789,6 +789,7 @@ impl RenderOnce for Dock {
|
||||
let titlebar = Titlebar {
|
||||
window_controls: self.window_controls,
|
||||
traffic_light_pad: self.traffic_light_pad,
|
||||
height: cx.theme().titlebar_height,
|
||||
};
|
||||
let root = render_node(&self.id, &self.state, &tree, &titlebar, window, cx);
|
||||
|
||||
@@ -811,6 +812,7 @@ impl RenderOnce for Dock {
|
||||
struct Titlebar {
|
||||
window_controls: bool,
|
||||
traffic_light_pad: bool,
|
||||
height: Pixels,
|
||||
}
|
||||
|
||||
/// The right sidebar content of the dock's active panel.
|
||||
@@ -997,7 +999,7 @@ fn render_pane(
|
||||
let pane = pane.clone();
|
||||
let titlebar = *titlebar;
|
||||
let padding_x = metrics.dense_padding_x;
|
||||
move |_, _| {
|
||||
move |window, cx| {
|
||||
let selected = pane
|
||||
.active
|
||||
.as_ref()
|
||||
@@ -1074,20 +1076,28 @@ fn render_pane(
|
||||
})
|
||||
.flex_none()
|
||||
.w_full()
|
||||
.h(titlebar.height)
|
||||
.items_center()
|
||||
.window_control_area(WindowControlArea::Drag)
|
||||
.on_click(maximize_on_double_click())
|
||||
.when(titlebar.traffic_light_pad, |bar| {
|
||||
bar.pl(px(TRAFFIC_LIGHT_PAD))
|
||||
})
|
||||
.pr(padding_x)
|
||||
.child(tabs)
|
||||
.when(titlebar.window_controls, |bar| {
|
||||
bar.child(div().flex_1())
|
||||
.child(WindowControls::new(SharedString::from(format!(
|
||||
"{dock_id}-pane-{}-controls",
|
||||
pane.key
|
||||
))))
|
||||
bar.child(titlebar_strip(
|
||||
div()
|
||||
.id(SharedString::from(format!(
|
||||
"{dock_id}-pane-{}-strip",
|
||||
pane.key
|
||||
)))
|
||||
.flex_1(),
|
||||
window,
|
||||
cx,
|
||||
))
|
||||
.child(WindowControls::new(SharedString::from(format!(
|
||||
"{dock_id}-pane-{}-controls",
|
||||
pane.key
|
||||
))))
|
||||
})
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
use gpui::prelude::FluentBuilder as _;
|
||||
use gpui::{
|
||||
AnyElement, App, AppContext as _, Context, ElementId, Entity, InteractiveElement as _,
|
||||
IntoElement, ParentElement as _, Pixels, RenderOnce, SharedString,
|
||||
StatefulInteractiveElement as _, Styled as _, Window, WindowControlArea, div, px,
|
||||
IntoElement, ParentElement as _, Pixels, RenderOnce, SharedString, Styled as _, Window, div,
|
||||
px,
|
||||
};
|
||||
use gpui_native_core::button::{Button, ButtonVariant};
|
||||
use gpui_native_core::common::{Sizable as _, h_flex, v_flex};
|
||||
@@ -14,7 +14,7 @@ use gpui_native_icons::IconRole;
|
||||
use gpui_native_theme::{ActiveTheme as _, Size};
|
||||
|
||||
use crate::resize_handle::ResizeHandle;
|
||||
use crate::window_controls::{TRAFFIC_LIGHT_PAD, WindowControls, maximize_on_double_click};
|
||||
use crate::window_controls::{TRAFFIC_LIGHT_PAD, WindowControls, titlebar_strip};
|
||||
use crate::window_frame::frame;
|
||||
|
||||
/// The width a sidebar opens at.
|
||||
@@ -207,6 +207,8 @@ impl RenderOnce for Shell {
|
||||
&state,
|
||||
metrics.strip_height,
|
||||
metrics.dense_padding_x,
|
||||
window,
|
||||
cx,
|
||||
))
|
||||
.child(ResizeHandle::new((id.clone(), "left-resize")).on_resize({
|
||||
let state = state.clone();
|
||||
@@ -239,6 +241,8 @@ impl RenderOnce for Shell {
|
||||
&state,
|
||||
metrics.strip_height,
|
||||
metrics.dense_padding_x,
|
||||
window,
|
||||
cx,
|
||||
))
|
||||
});
|
||||
|
||||
@@ -248,6 +252,7 @@ impl RenderOnce for Shell {
|
||||
}
|
||||
|
||||
/// One sidebar: a header strip with the hide toggle over the app's content.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn sidebar_region(
|
||||
width: Pixels,
|
||||
content: AnyElement,
|
||||
@@ -255,6 +260,8 @@ fn sidebar_region(
|
||||
state: &Entity<LayoutState>,
|
||||
strip_height: Pixels,
|
||||
padding_x: Pixels,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> AnyElement {
|
||||
let strip = h_flex()
|
||||
.id(SharedString::from(format!("sidebar-strip-{}", side.name())))
|
||||
@@ -263,8 +270,7 @@ fn sidebar_region(
|
||||
.h(strip_height)
|
||||
.items_center()
|
||||
.justify_end()
|
||||
.window_control_area(WindowControlArea::Drag)
|
||||
.on_click(maximize_on_double_click())
|
||||
.map(|strip| titlebar_strip(strip, window, cx))
|
||||
.map(|strip| match side {
|
||||
Side::Left => strip.when(cfg!(target_os = "macos"), |strip| {
|
||||
strip.pl(px(TRAFFIC_LIGHT_PAD))
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
//! The window controls: minimize, maximize/restore, close — in the platform's
|
||||
//! own dress and behavior. The titlebar strips drag the window themselves via
|
||||
//! `WindowControlArea::Drag`; macOS draws its traffic lights in the shell's
|
||||
//! reserved pad and needs no controls of ours.
|
||||
//! own dress and behavior. Titlebar strips are app-owned: they move the
|
||||
//! window themselves via [`titlebar_strip`], so a dragged tab or button press
|
||||
//! never moves it; macOS draws its traffic lights in the shell's reserved pad
|
||||
//! and needs no controls of ours.
|
||||
|
||||
use gpui::{App, ClickEvent, ElementId, IntoElement, RenderOnce, Window, div};
|
||||
use gpui::WindowControlArea;
|
||||
use gpui::{App, ClickEvent, Div, ElementId, IntoElement, RenderOnce, Stateful, Window, div};
|
||||
use gpui::{InteractiveElement as _, MouseButton, StatefulInteractiveElement as _};
|
||||
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
use gpui::prelude::FluentBuilder as _;
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
use gpui::{
|
||||
AnyElement, InteractiveElement as _, ParentElement as _, StatefulInteractiveElement as _,
|
||||
Styled as _,
|
||||
};
|
||||
use gpui::{AnyElement, ParentElement as _};
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
use gpui_native_icons::{IconRole, icon};
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
@@ -20,7 +20,7 @@ use gpui_native_theme::ActiveTheme as _;
|
||||
#[cfg(target_os = "linux")]
|
||||
use gpui::Decorations;
|
||||
#[cfg(target_os = "windows")]
|
||||
use gpui::{WindowControlArea, px, rgba};
|
||||
use gpui::rgba;
|
||||
|
||||
/// macOS traffic-lights zone: the pad a leading titlebar strip reserves.
|
||||
pub const TRAFFIC_LIGHT_PAD: f32 = 78.0;
|
||||
@@ -164,6 +164,40 @@ pub fn maximize_on_double_click() -> impl Fn(&ClickEvent, &mut Window, &mut App)
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes an element a titlebar strip: pressing and dragging it moves the
|
||||
/// window, double-clicking maximizes, and Windows keeps its native caption
|
||||
/// behavior through the `Drag` control area.
|
||||
pub fn titlebar_strip(this: Stateful<Div>, window: &mut Window, cx: &mut App) -> Stateful<Div> {
|
||||
let should_move = window.use_state(cx, |_, _| false);
|
||||
this.window_control_area(WindowControlArea::Drag)
|
||||
.on_mouse_down_out(window.listener_for(&should_move, |should_move, _, _, _| {
|
||||
*should_move = false;
|
||||
}))
|
||||
.on_mouse_down(
|
||||
MouseButton::Left,
|
||||
window.listener_for(&should_move, |should_move, _, _, _| {
|
||||
*should_move = true;
|
||||
}),
|
||||
)
|
||||
.on_mouse_up(
|
||||
MouseButton::Left,
|
||||
window.listener_for(&should_move, |should_move, _, _, _| {
|
||||
*should_move = false;
|
||||
}),
|
||||
)
|
||||
.on_mouse_move(
|
||||
window.listener_for(&should_move, |should_move, _, window, cx| {
|
||||
if *should_move {
|
||||
*should_move = false;
|
||||
if !cx.has_active_drag() {
|
||||
window.start_window_move();
|
||||
}
|
||||
}
|
||||
}),
|
||||
)
|
||||
.on_click(maximize_on_double_click())
|
||||
}
|
||||
|
||||
/// One Windows caption button.
|
||||
#[cfg(target_os = "windows")]
|
||||
fn caption_button(
|
||||
|
||||
Reference in New Issue
Block a user