chore: audit components

This commit is contained in:
2026-11-10 15:06:16 +07:00
parent af2177c6c8
commit 03369f501e
92 changed files with 2744 additions and 1947 deletions
+2 -1
View File
@@ -8,7 +8,7 @@ description = "Application layout bootstrap: sidebars and a center dock, one car
gpui = { workspace = true }
gpui-native-theme = { workspace = true }
gpui-native-utils = { workspace = true }
gpui-native-core = { workspace = true, optional = true, features = ["button"] }
gpui-native-core = { workspace = true, optional = true }
gpui-native-data = { workspace = true, optional = true, features = ["sidebar"] }
gpui-native-icons = { workspace = true, optional = true }
@@ -33,6 +33,7 @@ shell = [
"resize_handle",
"window_controls",
"dep:gpui-native-core",
"gpui-native-core/button",
"dep:gpui-native-data",
"dep:gpui-native-icons",
]
+48 -56
View File
@@ -32,6 +32,7 @@ type PanelBuilder = Box<dyn Fn(&mut Window, &mut App) -> AnyView>;
type OpenRightSidebarHandler = Rc<dyn Fn(&mut App)>;
/// A registered panel: its identity, chrome, and how its view is built.
#[must_use]
pub struct PanelDescriptor {
id: SharedString,
title: SharedString,
@@ -237,7 +238,7 @@ impl DockState {
/// Points the right sidebar at the panel's own view and asks the app to open it.
fn show_right_view_of(&mut self, id: &str, cx: &mut Context<Self>) {
let right_view = tab_in(&self.root, id).and_then(|tab| tab.right_view.clone());
let right_view = Self::tab_in(&self.root, id).and_then(|tab| tab.right_view.clone());
let Some(right_view) = right_view else {
return;
};
@@ -300,7 +301,7 @@ impl DockState {
// The sidebar showed the closed panel's view.
if self.right_view.as_ref() == closed_right_view.as_ref() {
self.right_view = first_right_view(&self.root);
self.right_view = Self::first_right_view(&self.root);
}
if emptied {
@@ -341,7 +342,7 @@ impl DockState {
})
})
.and_then(|tab| tab.right_view.clone())
.or_else(|| first_right_view(&self.root));
.or_else(|| Self::first_right_view(&self.root));
cx.notify();
}
}
@@ -372,7 +373,7 @@ impl DockState {
}
// The closed pane's views are gone; show the first remaining one.
self.right_view = first_right_view(&self.root);
self.right_view = Self::first_right_view(&self.root);
cx.notify();
}
@@ -495,7 +496,7 @@ impl DockState {
/// The active tab of the pane new panels open into.
pub fn active_panel(&self) -> Option<SharedString> {
let pane = pane_in(&self.root, self.target_pane_key())?;
let pane = Self::pane_in(&self.root, self.target_pane_key())?;
pane.active.clone()
}
@@ -522,7 +523,7 @@ impl DockState {
}
fn target_pane_key(&self) -> u64 {
if pane_in(&self.root, self.active_pane).is_some() {
if Self::pane_in(&self.root, self.active_pane).is_some() {
self.active_pane
} else {
self.first_pane_key().unwrap_or(0)
@@ -613,7 +614,7 @@ impl DockState {
) {
match node {
Node::Pane(pane) if pane.key == key => {
let old_pane = take_node(node);
let old_pane = Self::take_node(node);
let split = if direction.leads() {
Split {
key: split_key,
@@ -635,9 +636,9 @@ impl DockState {
}
Node::Pane(_) => {}
Node::Split(split) => {
if pane_in(&split.a, key).is_some() {
if Self::pane_in(&split.a, key).is_some() {
Self::wrap_in(&mut split.a, key, new_pane, direction, split_key);
} else if pane_in(&split.b, key).is_some() {
} else if Self::pane_in(&split.b, key).is_some() {
Self::wrap_in(&mut split.b, key, new_pane, direction, split_key);
}
}
@@ -654,14 +655,14 @@ impl DockState {
return;
};
let a_direct = direct_pane_key(&split.a) == Some(key);
let b_direct = direct_pane_key(&split.b) == Some(key);
let a_direct = Self::direct_pane_key(&split.a) == Some(key);
let b_direct = Self::direct_pane_key(&split.b) == Some(key);
if a_direct != b_direct {
let survivor = if a_direct {
take_node(&mut split.b)
Self::take_node(&mut split.b)
} else {
take_node(&mut split.a)
Self::take_node(&mut split.a)
};
*node = survivor;
} else {
@@ -699,52 +700,46 @@ impl DockState {
}
snap(&self.root)
}
}
/// Finds a pane by key anywhere in the tree.
fn pane_in(node: &Node, key: u64) -> Option<&Pane> {
match node {
Node::Pane(pane) => (pane.key == key).then_some(pane),
Node::Split(split) => pane_in(&split.a, key).or_else(|| pane_in(&split.b, key)),
/// Finds an open tab by panel id, anywhere in the tree.
fn tab_in<'a>(node: &'a Node, id: &str) -> Option<&'a PanelTab> {
match node {
Node::Pane(pane) => pane.tabs.iter().find(|tab| tab.panel.as_ref() == id),
Node::Split(split) => Self::tab_in(&split.a, id).or_else(|| Self::tab_in(&split.b, id)),
}
}
}
/// Finds an open tab by panel id, anywhere in the tree.
fn tab_in<'a>(node: &'a Node, id: &str) -> Option<&'a PanelTab> {
match node {
Node::Pane(pane) => pane.tabs.iter().find(|tab| tab.panel.as_ref() == id),
Node::Split(split) => tab_in(&split.a, id).or_else(|| tab_in(&split.b, id)),
/// The first open tab's right sidebar view, in display order.
fn first_right_view(node: &Node) -> Option<AnyView> {
match node {
Node::Pane(pane) => pane.tabs.iter().find_map(|tab| tab.right_view.clone()),
Node::Split(split) => {
Self::first_right_view(&split.a).or_else(|| Self::first_right_view(&split.b))
}
}
}
}
/// The first open tab's right sidebar view, in display order.
fn first_right_view(node: &Node) -> Option<AnyView> {
match node {
Node::Pane(pane) => pane.tabs.iter().find_map(|tab| tab.right_view.clone()),
Node::Split(split) => first_right_view(&split.a).or_else(|| first_right_view(&split.b)),
/// The pane key of a node that is a pane, for seam-level tree edits.
fn direct_pane_key(node: &Node) -> Option<u64> {
match node {
Node::Pane(pane) => Some(pane.key),
Node::Split(_) => None,
}
}
}
/// The pane key of a node that is a pane, for seam-level tree edits.
fn direct_pane_key(node: &Node) -> Option<u64> {
match node {
Node::Pane(pane) => Some(pane.key),
Node::Split(_) => None,
/// Takes a node out, leaving a placeholder that is never read again.
fn take_node(node: &mut Node) -> Node {
std::mem::replace(
node,
Node::Pane(Pane {
key: u64::MAX,
tabs: Vec::new(),
active: None,
}),
)
}
}
/// Takes a node out, leaving a placeholder that is never read again.
fn take_node(node: &mut Node) -> Node {
std::mem::replace(
node,
Node::Pane(Pane {
key: u64::MAX,
tabs: Vec::new(),
active: None,
}),
)
}
/// One open panel view, per frame.
#[derive(Clone)]
struct TabView {
@@ -776,6 +771,7 @@ enum TreeView {
/// A dock: renders the pane tree of a [`DockState`].
#[derive(IntoElement)]
#[must_use]
pub struct Dock {
id: ElementId,
state: Entity<DockState>,
@@ -1095,6 +1091,7 @@ fn render_pane(
});
let bar_id = SharedString::from(format!("{dock_id}-pane-{}-bar", pane.key));
let strip_id = SharedString::from(format!("{dock_id}-pane-{}-strip", pane.key));
h_flex()
.id(bar_id.clone())
@@ -1111,13 +1108,8 @@ fn render_pane(
.pr(padding_x)
.child(tabs)
.child(titlebar_strip(
div()
.id(SharedString::from(format!(
"{dock_id}-pane-{}-strip",
pane.key
)))
.flex_1()
.self_stretch(),
div().id(strip_id.clone()).flex_1().self_stretch(),
strip_id,
window,
cx,
))
+14 -11
View File
@@ -6,15 +6,11 @@ use gpui::{
InteractiveElement as _, IntoElement, ParentElement as _, Pixels, RenderOnce, SharedString,
StyleRefinement, Styled, Window, canvas, div, px,
};
use gpui_native_theme::{PANE_DEFAULT_SIZE, PANE_MIN_SIZE};
use gpui_native_utils::StyledExt as _;
use crate::resize_handle::ResizeHandle;
/// The size a pane starts at before the group has measured its extent.
const DEFAULT_PANE_SIZE: f32 = 200.0;
/// The floor a pane keeps while dragging unless the caller lowers it.
const DEFAULT_PANE_MIN: f32 = 48.0;
type DebugSelector = std::rc::Rc<dyn Fn() -> String>;
/// The retained pane sizes of one resizable group.
@@ -56,11 +52,12 @@ impl ResizableState {
}
}
/// The equal split of the measured extent.
/// The equal split of the measured extent, or the default pane size unmeasured.
fn equal_split(&self, pane_count: usize) -> Pixels {
match self.measured {
Some(extent) => extent / pane_count as f32,
None => px(DEFAULT_PANE_SIZE),
// usize can exceed f32's exact integer range; bound it via u32 before casting.
Some(extent) => extent / u32::try_from(pane_count).unwrap_or(u32::MAX) as f32,
None => PANE_DEFAULT_SIZE,
}
}
@@ -93,8 +90,11 @@ impl ResizableState {
size = size.min(ceiling);
}
// Earlier panes hold the equal split they already render at, so the
// first drag does not snap them to a backfilled default.
let backfill = self.equal_split(pane_count);
while self.sizes.len() <= ix {
self.sizes.push(px(DEFAULT_PANE_SIZE));
self.sizes.push(backfill);
}
self.sizes[ix] = size;
@@ -105,7 +105,7 @@ impl ResizableState {
fn reset_pane(&mut self, ix: usize, pane_count: usize, cx: &mut Context<Self>) {
let split = self.equal_split(pane_count);
while self.sizes.len() <= ix {
self.sizes.push(px(DEFAULT_PANE_SIZE));
self.sizes.push(split);
}
self.sizes[ix] = split;
cx.notify();
@@ -113,6 +113,7 @@ impl ResizableState {
}
/// One pane of a resizable group: its content and its drag floor.
#[must_use]
pub struct ResizablePane {
content: AnyElement,
min: Pixels,
@@ -120,7 +121,7 @@ pub struct ResizablePane {
impl ResizablePane {
/// The floor a pane keeps while dragging unless the caller lowers it.
pub const DEFAULT_MIN: Pixels = px(DEFAULT_PANE_MIN);
pub const DEFAULT_MIN: Pixels = PANE_MIN_SIZE;
/// Wraps this element as a pane.
pub fn new<E>(content: E) -> Self
@@ -142,6 +143,7 @@ impl ResizablePane {
/// A group of panes stacked along one axis, separated by drag dividers.
#[derive(IntoElement)]
#[must_use]
pub struct Resizable {
id: ElementId,
state: Entity<ResizableState>,
@@ -250,6 +252,7 @@ impl RenderOnce for Resizable {
.into_any_element();
let divider = (!remainder).then(|| {
// The positional index is a stable id: panes are append-only, never reordered.
ResizeHandle::new(SharedString::from(format!("{}-divider-{ix}", self.id)))
.axis(axis)
.on_resize({
+12 -9
View File
@@ -10,7 +10,9 @@ use gpui::{
SharedString, StatefulInteractiveElement as _, Styled as _, Window, canvas, deferred, div,
fill, point, px,
};
use gpui_native_theme::{ActiveTheme as _, RESIZE_KEYBOARD_STEP, Size};
use gpui_native_theme::{
ActiveTheme as _, BORDER_WIDTH, HAIRLINE_WIDTH, RESIZE_KEYBOARD_STEP, Size,
};
use gpui_native_utils::{focus_handle, focus_ring};
/// How far one keyboard arrow step moves the seam.
@@ -28,12 +30,13 @@ struct DragState {
/// A thin drag divider that resizes the adjacent region.
#[derive(IntoElement)]
#[must_use]
pub struct ResizeHandle {
id: ElementId,
/// The axis the drag moves along: a vertical seam drags horizontally.
axis: Axis,
disabled: bool,
a11y_label: Option<SharedString>,
accessibility_label: Option<SharedString>,
on_resize: Option<ResizeHandler>,
on_reset: Option<ResetHandler>,
}
@@ -48,7 +51,7 @@ impl ResizeHandle {
id: id.into(),
axis: Axis::Horizontal,
disabled: false,
a11y_label: None,
accessibility_label: None,
on_resize: None,
on_reset: None,
}
@@ -71,7 +74,7 @@ impl ResizeHandle {
where
L: Into<SharedString>,
{
self.a11y_label = Some(label.into());
self.accessibility_label = Some(label.into());
self
}
@@ -105,7 +108,7 @@ impl RenderOnce for ResizeHandle {
let straddle = -thickness / 2.0;
let label = self
.a11y_label
.accessibility_label
.clone()
.unwrap_or_else(|| SharedString::from("Resize"));
@@ -221,17 +224,17 @@ impl RenderOnce for ResizeHandle {
let line = match axis {
Axis::Horizontal => {
let x = bounds.center().x - px(0.5);
let x = bounds.center().x - HAIRLINE_WIDTH;
Bounds::from_corners(
point(x, bounds.top()),
point(x + px(1.), bounds.bottom()),
point(x + BORDER_WIDTH, bounds.bottom()),
)
}
Axis::Vertical => {
let y = bounds.center().y - px(0.5);
let y = bounds.center().y - HAIRLINE_WIDTH;
Bounds::from_corners(
point(bounds.left(), y),
point(bounds.right(), y + px(1.)),
point(bounds.right(), y + BORDER_WIDTH),
)
}
};
+53 -40
View File
@@ -10,19 +10,14 @@ use gpui_native_core::button::{Button, ButtonVariant};
use gpui_native_core::common::{Sizable as _, h_flex, v_flex};
use gpui_native_data::sidebar::Sidebar;
use gpui_native_icons::IconRole;
use gpui_native_theme::{ActiveTheme as _, Size};
use gpui_native_theme::{
ActiveTheme as _, SIDEBAR_DEFAULT_WIDTH, SIDEBAR_MAX_WIDTH, SIDEBAR_MIN_WIDTH, Size,
};
use crate::resize_handle::ResizeHandle;
use crate::window_controls::{TRAFFIC_LIGHT_PAD, WindowControls, titlebar_strip};
use crate::window_frame::frame;
/// The width a sidebar opens at.
const DEFAULT_SIDEBAR_WIDTH: f32 = 240.0;
/// The narrowest a sidebar can be dragged to.
const MIN_SIDEBAR_WIDTH: f32 = 160.0;
/// The widest a sidebar can be dragged to.
const MAX_SIDEBAR_WIDTH: f32 = 480.0;
/// The layout the application owns.
pub struct LayoutState {
left_visible: bool,
@@ -37,8 +32,8 @@ impl LayoutState {
cx.new(|_| Self {
left_visible: true,
right_visible: false,
left_width: px(DEFAULT_SIDEBAR_WIDTH),
right_width: px(DEFAULT_SIDEBAR_WIDTH),
left_width: SIDEBAR_DEFAULT_WIDTH,
right_width: SIDEBAR_DEFAULT_WIDTH,
})
}
@@ -80,16 +75,20 @@ impl LayoutState {
/// Applies a drag delta to the left sidebar's width, clamped.
fn resize_left(&mut self, delta: Pixels, cx: &mut Context<Self>) {
self.left_width =
(self.left_width + delta).clamp(px(MIN_SIDEBAR_WIDTH), px(MAX_SIDEBAR_WIDTH));
cx.notify();
let width = (self.left_width + delta).clamp(SIDEBAR_MIN_WIDTH, SIDEBAR_MAX_WIDTH);
if (self.left_width - width).abs() > px(f32::EPSILON) {
self.left_width = width;
cx.notify();
}
}
/// Applies a drag delta to the right sidebar's width, clamped.
fn resize_right(&mut self, delta: Pixels, cx: &mut Context<Self>) {
self.right_width =
(self.right_width - delta).clamp(px(MIN_SIDEBAR_WIDTH), px(MAX_SIDEBAR_WIDTH));
cx.notify();
let width = (self.right_width - delta).clamp(SIDEBAR_MIN_WIDTH, SIDEBAR_MAX_WIDTH);
if (self.right_width - width).abs() > px(f32::EPSILON) {
self.right_width = width;
cx.notify();
}
}
}
@@ -111,6 +110,7 @@ impl Side {
/// The application shell.
#[derive(IntoElement)]
#[must_use]
pub struct Shell {
id: ElementId,
state: Entity<LayoutState>,
@@ -194,11 +194,13 @@ impl RenderOnce for Shell {
left.take()
.map(|sidebar| sidebar.into_any_element())
.unwrap_or_else(|| div().into_any_element()),
Side::Left,
left_material,
&state,
titlebar_height,
metrics.dense_padding_x,
Titlebar {
side: Side::Left,
material: left_material,
height: titlebar_height,
padding_x: metrics.dense_padding_x,
},
window,
cx,
))
@@ -231,11 +233,13 @@ impl RenderOnce for Shell {
(id.clone(), "right"),
width,
right.take().unwrap_or_else(|| div().into_any_element()),
Side::Right,
false,
&state,
titlebar_height,
metrics.dense_padding_x,
Titlebar {
side: Side::Right,
material: false,
height: titlebar_height,
padding_x: metrics.dense_padding_x,
},
window,
cx,
))
@@ -246,48 +250,57 @@ 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(
id: impl Into<ElementId>,
width: Pixels,
content: AnyElement,
/// Which titlebar duties a sidebar's header strip carries.
#[derive(Clone, Copy)]
struct Titlebar {
side: Side,
material: bool,
state: &Entity<LayoutState>,
strip_height: Pixels,
height: Pixels,
padding_x: Pixels,
}
/// One sidebar: a header strip with the hide toggle over the app's content.
fn sidebar_region<I>(
id: I,
width: Pixels,
content: AnyElement,
state: &Entity<LayoutState>,
titlebar: Titlebar,
window: &mut Window,
cx: &mut App,
) -> AnyElement {
) -> AnyElement
where
I: Into<ElementId>,
{
let id = id.into();
let theme = cx.theme();
let background = theme.colors.background;
let strip_background = match (side, material) {
let strip_background = match (titlebar.side, titlebar.material) {
(Side::Left, true) => theme.material.background,
_ => theme.colors.background,
};
let strip = h_flex()
.id((id.clone(), "strip"))
.debug_selector(move || format!("sidebar-strip-{}", side.name()))
.debug_selector(move || format!("sidebar-strip-{}", titlebar.side.name()))
.flex_none()
.h(strip_height)
.h(titlebar.height)
.bg(strip_background)
.items_center()
.map(|strip| match side {
.map(|strip| match titlebar.side {
Side::Left => strip.when(cfg!(target_os = "macos"), |strip| {
strip.pl(TRAFFIC_LIGHT_PAD)
}),
Side::Right => strip,
})
.px(padding_x)
.px(titlebar.padding_x)
.child(titlebar_strip(
div().id((id.clone(), "strip-drag")).flex_1().self_stretch(),
(id.clone(), "strip-drag"),
window,
cx,
))
.map(|strip| match side {
.map(|strip| match titlebar.side {
Side::Left => strip.child(
Button::new((id, "sidebar-toggle"))
.icon(IconRole::PanelLeft)
@@ -314,7 +327,7 @@ fn sidebar_region(
.flex_1()
.min_h_0()
.overflow_hidden()
.when(!material, |content| content.bg(background))
.when(!titlebar.material, |content| content.bg(background))
.child(content),
)
.into_any_element()
+31 -19
View File
@@ -8,6 +8,8 @@ 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::Styled as _;
#[cfg(any(target_os = "windows", target_os = "linux"))]
use gpui::prelude::FluentBuilder as _;
#[cfg(any(target_os = "windows", target_os = "linux"))]
@@ -19,13 +21,18 @@ use gpui_native_theme::ActiveTheme as _;
#[cfg(target_os = "linux")]
use gpui::Decorations;
#[cfg(any(target_os = "windows", target_os = "linux"))]
use gpui::SharedString;
#[cfg(target_os = "windows")]
use gpui::{SharedString, px, rgba};
use gpui::px;
/// macOS traffic-lights zone: the pad a leading titlebar strip reserves.
pub use gpui_native_theme::TRAFFIC_LIGHT_PAD;
#[cfg(target_os = "windows")]
use gpui_native_theme::{WINDOWS_CAPTION_WIDTH as CAPTION_WIDTH, WINDOWS_CLOSE_HOVER_HEX};
use gpui_native_theme::{
WINDOWS_CAPTION_WIDTH as CAPTION_WIDTH, WINDOWS_CLOSE_FOREGROUND, WINDOWS_CLOSE_HOVER,
WINDOWS_CLOSE_PRESSED,
};
/// The window controls for the trailing end of a titlebar strip.
#[derive(IntoElement)]
@@ -49,6 +56,7 @@ impl RenderOnce for WindowControls {
#[cfg(target_os = "macos")]
let controls = {
// macOS draws native traffic lights in the shell's reserved pad; nothing to render.
let _ = (id, window, cx);
div()
};
@@ -97,7 +105,6 @@ impl RenderOnce for WindowControls {
#[cfg(target_os = "linux")]
let controls = {
let _ = id;
let controls = window.window_controls();
let client_decorated =
matches!(window.window_decorations(), Decorations::Client { .. });
@@ -111,7 +118,7 @@ impl RenderOnce for WindowControls {
.when(client_decorated && controls.minimize, |row| {
row.child(circle_button(
cx,
"window-minimize",
SharedString::from(format!("{id}-minimize")),
IconRole::WindowMinimize,
window.is_minimizable(),
|window, _cx| window.minimize_window(),
@@ -120,7 +127,7 @@ impl RenderOnce for WindowControls {
.when(client_decorated && controls.maximize, |row| {
row.child(circle_button(
cx,
"window-maximize",
SharedString::from(format!("{id}-maximize")),
if window.is_maximized() {
IconRole::WindowRestore
} else {
@@ -133,7 +140,7 @@ impl RenderOnce for WindowControls {
.when(client_decorated, |row| {
row.child(circle_button(
cx,
"window-close",
SharedString::from(format!("{id}-close")),
IconRole::Close,
true,
|window, _cx| window.remove_window(),
@@ -155,6 +162,7 @@ fn maximize_on_double_click() -> impl Fn(&ClickEvent, &mut Window, &mut App) + '
window.titlebar_double_click();
#[cfg(target_os = "linux")]
window.zoom_window();
// Windows handles titlebar double-click natively via WindowControlArea::Drag.
#[cfg(target_os = "windows")]
{
let _ = (event, window);
@@ -162,9 +170,18 @@ fn maximize_on_double_click() -> impl Fn(&ClickEvent, &mut Window, &mut App) + '
}
}
/// Makes an element a titlebar strip.
pub fn titlebar_strip(this: Stateful<Div>, window: &mut Window, cx: &mut App) -> Stateful<Div> {
let should_move = window.use_state(cx, |_, _| false);
/// Makes an element a titlebar strip; `id` keys its drag state, which
/// `use_state` would otherwise share among every strip in the window.
pub fn titlebar_strip<I>(
this: Stateful<Div>,
id: I,
window: &mut Window,
cx: &mut App,
) -> Stateful<Div>
where
I: Into<ElementId>,
{
let should_move = window.use_keyed_state(id.into(), cx, |_, _| false);
this.window_control_area(WindowControlArea::Drag)
.on_mouse_down_out(window.listener_for(&should_move, |should_move, _, _, _| {
*should_move = false;
@@ -208,7 +225,7 @@ fn caption_button(
let glyph_size = theme.sizes.small.caption_size;
let hover = if close {
rgba(WINDOWS_CLOSE_HOVER_HEX)
WINDOWS_CLOSE_HOVER
} else {
theme.colors.muted_hover
};
@@ -236,7 +253,7 @@ fn caption_button(
.cursor_pointer()
.hover(|hovered| {
hovered.bg(hover).text_color(if close {
gpui::white()
WINDOWS_CLOSE_FOREGROUND
} else {
theme.colors.foreground
})
@@ -244,7 +261,7 @@ fn caption_button(
.active(|pressed| {
pressed
.bg(if close {
hover.opacity(0.8)
WINDOWS_CLOSE_PRESSED
} else {
theme.ramps.secondary.pressed
})
@@ -257,14 +274,9 @@ fn caption_button(
/// Adwaita's headerbar control: a small circle around a symbolic glyph.
#[cfg(target_os = "linux")]
fn circle_button<F>(
cx: &App,
id: &'static str,
role: IconRole,
enabled: bool,
action: F,
) -> AnyElement
fn circle_button<I, F>(cx: &App, id: I, role: IconRole, enabled: bool, action: F) -> AnyElement
where
I: Into<ElementId>,
F: Fn(&mut Window, &mut App) + 'static,
{
let theme = cx.theme();
+7 -5
View File
@@ -7,13 +7,15 @@ use gpui::{
IntoElement, MouseButton, ParentElement as _, Pixels, Point, ResizeEdge, Size, Styled as _,
Tiling, Window, canvas, div, point, px, size,
};
use gpui_native_theme::{ActiveTheme as _, BORDER_WIDTH, WINDOW_RESIZE_INSET};
/// How much of the shadow's strength survives losing window focus.
const INACTIVE_SHADOW_OPACITY: f32 = 0.7;
use gpui_native_theme::{
ActiveTheme as _, BORDER_WIDTH, INACTIVE_SHADOW_OPACITY, WINDOW_RESIZE_INSET,
};
/// Wraps a window's root in the frame it owes.
pub fn frame(child: impl IntoElement, window: &mut Window, cx: &App) -> Div {
pub fn frame<E>(child: E, window: &mut Window, cx: &App) -> Div
where
E: IntoElement,
{
let Decorations::Client { tiling } = window.window_decorations() else {
return div().size_full().child(child);
};
+2
View File
@@ -1,3 +1,5 @@
#![cfg(feature = "dock")]
use gpui::{
AppContext as _, Context, Entity, InteractiveElement as _, IntoElement, Modifiers, MouseButton,
MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement as _, Pixels, Render, Styled as _,
+2
View File
@@ -1,3 +1,5 @@
#![cfg(feature = "resizable")]
use gpui::{
AppContext as _, Context, Entity, IntoElement, Modifiers, MouseButton, MouseDownEvent,
MouseMoveEvent, ParentElement as _, Pixels, Render, Styled as _, Subscription, TestAppContext,
+2
View File
@@ -1,3 +1,5 @@
#![cfg(feature = "resize_handle")]
use std::cell::RefCell;
use std::rc::Rc;
+2
View File
@@ -1,3 +1,5 @@
#![cfg(feature = "shell")]
use gpui::{
Entity, InteractiveElement as _, Modifiers, MouseButton, MouseDownEvent, MouseMoveEvent,
MouseUpEvent, ParentElement as _, Pixels, Render, Styled as _, TestAppContext,
+2
View File
@@ -1,6 +1,8 @@
//! The titlebar duties: traffic-light pads and window controls, asserted per
//! platform — macOS draws only pads, Windows/Linux only draw their controls.
#![cfg(feature = "dock")]
use gpui::{
AppContext as _, Entity, IntoElement, ParentElement as _, Pixels, Render, Styled as _,
TestAppContext, VisualTestContext, Window, div, px,