update
This commit is contained in:
@@ -172,7 +172,6 @@ impl Global for GlobalAppSettings {}
|
|||||||
pub struct AppSettings {
|
pub struct AppSettings {
|
||||||
/// Settings
|
/// Settings
|
||||||
inner: Entity<Settings>,
|
inner: Entity<Settings>,
|
||||||
|
|
||||||
/// Event subscriptions
|
/// Event subscriptions
|
||||||
_subscriptions: SmallVec<[Subscription; 2]>,
|
_subscriptions: SmallVec<[Subscription; 2]>,
|
||||||
}
|
}
|
||||||
@@ -184,8 +183,6 @@ impl AppSettings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The underlying settings entity, which notifies whenever any field changes.
|
/// The underlying settings entity, which notifies whenever any field changes.
|
||||||
/// Settings load asynchronously, so observers can watch it to pick up values
|
|
||||||
/// that arrive after construction.
|
|
||||||
pub fn entity(&self) -> &Entity<Settings> {
|
pub fn entity(&self) -> &Entity<Settings> {
|
||||||
&self.inner
|
&self.inner
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ pub const CLIENT_SIDE_DECORATION_BORDER: Pixels = px(1.0);
|
|||||||
pub const TITLEBAR_HEIGHT: Pixels = px(36.0);
|
pub const TITLEBAR_HEIGHT: Pixels = px(36.0);
|
||||||
|
|
||||||
/// Defines workspace tabbar height
|
/// Defines workspace tabbar height
|
||||||
pub const TABBAR_HEIGHT: Pixels = px(44.0);
|
pub const TABBAR_HEIGHT: Pixels = px(36.0);
|
||||||
|
|
||||||
/// Defines default sidebar width
|
/// Defines default sidebar width
|
||||||
pub const SIDEBAR_WIDTH: Pixels = px(240.);
|
pub const SIDEBAR_WIDTH: Pixels = px(240.);
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ use crate::menu::DropdownMenu as _;
|
|||||||
use crate::resizable::{resize_handle, resize_handle_appearance};
|
use crate::resizable::{resize_handle, resize_handle_appearance};
|
||||||
use crate::tab::Tab;
|
use crate::tab::Tab;
|
||||||
use crate::tab::tab_bar::TabBar;
|
use crate::tab::tab_bar::TabBar;
|
||||||
use crate::title_bar::{title_bar_drag_handlers, window_controls};
|
use crate::title_bar::{TRAFFIC_LIGHT_PADDING, title_bar_drag_handlers, window_controls};
|
||||||
use crate::{IconName, Selectable, Sizable, StyledExt, h_flex, v_flex};
|
use crate::{IconName, Selectable, Sizable, StyledExt, h_flex, v_flex};
|
||||||
|
|
||||||
mod panel;
|
mod panel;
|
||||||
@@ -429,6 +429,42 @@ impl TabGroupSkin {
|
|||||||
== Some(group.node())
|
== Some(group.node())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether this group is the left dock's root with a single panel.
|
||||||
|
///
|
||||||
|
/// Such a group draws no tab bar, so its panel owns the window's top-left
|
||||||
|
/// corner — including the space the macOS traffic lights overlay.
|
||||||
|
fn is_plain_left_group(&self, group: &TabGroupContext, cx: &App) -> bool {
|
||||||
|
let Some(area) = self.shared.area() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
let area = area.read(cx);
|
||||||
|
|
||||||
|
area.layout(DockPlacement::Left)
|
||||||
|
.map(|tree| tree.root().id())
|
||||||
|
== Some(group.node())
|
||||||
|
&& group.panels().len() == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether this group is the topmost-left group on screen, which sits under
|
||||||
|
/// the native macOS traffic lights. The left dock's group is leftmost while
|
||||||
|
/// it is open and holds a panel; the center's is leftmost otherwise.
|
||||||
|
fn is_leftmost_top_group(&self, group: &TabGroupContext, cx: &App) -> bool {
|
||||||
|
let Some(area) = self.shared.area() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
let area = area.read(cx);
|
||||||
|
|
||||||
|
let left_open =
|
||||||
|
area.is_dock_open(DockPlacement::Left) && !area.is_empty(DockPlacement::Left, cx);
|
||||||
|
let tree = if left_open {
|
||||||
|
area.layout(DockPlacement::Left)
|
||||||
|
} else {
|
||||||
|
area.layout(DockPlacement::Center)
|
||||||
|
};
|
||||||
|
|
||||||
|
tree.and_then(|tree| left_top_group(tree.root())) == Some(group.node())
|
||||||
|
}
|
||||||
|
|
||||||
fn render_toolbar(
|
fn render_toolbar(
|
||||||
&self,
|
&self,
|
||||||
group: &TabGroupContext,
|
group: &TabGroupContext,
|
||||||
@@ -509,6 +545,8 @@ impl TabGroupSkin {
|
|||||||
let has_leading = left_button.is_some() || bottom_button.is_some();
|
let has_leading = left_button.is_some() || bottom_button.is_some();
|
||||||
let drag = tab_drag(group, ix, cx);
|
let drag = tab_drag(group, ix, cx);
|
||||||
let is_title_bar = self.is_title_bar_group(group, cx);
|
let is_title_bar = self.is_title_bar_group(group, cx);
|
||||||
|
let needs_traffic_light_padding =
|
||||||
|
cfg!(target_os = "macos") && self.is_leftmost_top_group(group, cx);
|
||||||
let trailing_chrome = is_title_bar
|
let trailing_chrome = is_title_bar
|
||||||
.then(|| self.shared.chrome.trailing(window, cx))
|
.then(|| self.shared.chrome.trailing(window, cx))
|
||||||
.flatten();
|
.flatten();
|
||||||
@@ -532,6 +570,9 @@ impl TabGroupSkin {
|
|||||||
.children(bottom_button),
|
.children(bottom_button),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
.when(needs_traffic_light_padding, |this| {
|
||||||
|
this.pl(px(TRAFFIC_LIGHT_PADDING))
|
||||||
|
})
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
.id("tab")
|
.id("tab")
|
||||||
@@ -612,6 +653,8 @@ impl TabGroupSkin {
|
|||||||
.position(|panel| panel.panel_id(cx) == displayed)
|
.position(|panel| panel.panel_id(cx) == displayed)
|
||||||
});
|
});
|
||||||
let is_title_bar = self.is_title_bar_group(group, cx);
|
let is_title_bar = self.is_title_bar_group(group, cx);
|
||||||
|
let needs_traffic_light_padding =
|
||||||
|
cfg!(target_os = "macos") && self.is_leftmost_top_group(group, cx);
|
||||||
let trailing_chrome = is_title_bar
|
let trailing_chrome = is_title_bar
|
||||||
.then(|| self.shared.chrome.trailing(window, cx))
|
.then(|| self.shared.chrome.trailing(window, cx))
|
||||||
.flatten();
|
.flatten();
|
||||||
@@ -640,6 +683,9 @@ impl TabGroupSkin {
|
|||||||
.track_scroll(&self.scroll_handle)
|
.track_scroll(&self.scroll_handle)
|
||||||
.h(TABBAR_HEIGHT)
|
.h(TABBAR_HEIGHT)
|
||||||
.bg(cx.theme().panel_background)
|
.bg(cx.theme().panel_background)
|
||||||
|
.when(needs_traffic_light_padding, |this| {
|
||||||
|
this.pl(px(TRAFFIC_LIGHT_PADDING))
|
||||||
|
})
|
||||||
.when(is_title_bar || has_leading, |this| {
|
.when(is_title_bar || has_leading, |this| {
|
||||||
this.prefix(
|
this.prefix(
|
||||||
h_flex()
|
h_flex()
|
||||||
@@ -834,6 +880,12 @@ impl TabGroupRenderer for TabGroupSkin {
|
|||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> AnyElement {
|
) -> AnyElement {
|
||||||
|
// The left dock's only panel draws bare, so its content can own the
|
||||||
|
// window's top-left corner instead of a tab bar doing so.
|
||||||
|
if self.is_plain_left_group(group, cx) {
|
||||||
|
return Empty.into_any_element();
|
||||||
|
}
|
||||||
|
|
||||||
let visible: Vec<usize> = group
|
let visible: Vec<usize> = group
|
||||||
.panels()
|
.panels()
|
||||||
.iter()
|
.iter()
|
||||||
|
|||||||
+13
-22
@@ -67,14 +67,10 @@ enum Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct Workspace {
|
pub struct Workspace {
|
||||||
sidebar: Entity<Sidebar>,
|
|
||||||
/// App's Dock Area
|
|
||||||
dock: Entity<DockArea>,
|
dock: Entity<DockArea>,
|
||||||
title_bar_chrome: Rc<dock::TitleBarChrome>,
|
title_bar_chrome: Rc<dock::TitleBarChrome>,
|
||||||
|
|
||||||
/// Async tasks
|
/// Async tasks
|
||||||
tasks: Vec<Task<Result<(), Error>>>,
|
tasks: Vec<Task<Result<(), Error>>>,
|
||||||
|
|
||||||
/// Event subscriptions
|
/// Event subscriptions
|
||||||
_subscriptions: SmallVec<[Subscription; 6]>,
|
_subscriptions: SmallVec<[Subscription; 6]>,
|
||||||
}
|
}
|
||||||
@@ -221,17 +217,25 @@ impl Workspace {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
cx.defer_in(window, |this, window, cx| {
|
cx.defer_in(window, move |this, window, cx| {
|
||||||
|
let sidebar = PanelHandle::new(sidebar);
|
||||||
|
|
||||||
|
this.dock.update(cx, |area, cx| {
|
||||||
|
let left = DockLayout::tabs().panel_view(Arc::new(sidebar), cx);
|
||||||
|
area.set_dock(DockPlacement::Left, left, window, cx);
|
||||||
|
area.set_dock_size(DockPlacement::Left, SIDEBAR_WIDTH, window, cx);
|
||||||
|
});
|
||||||
|
|
||||||
let greeter = PanelHandle::new(greeter::init(window, cx));
|
let greeter = PanelHandle::new(greeter::init(window, cx));
|
||||||
let center = DockLayout::v_split()
|
let center = DockLayout::v_split()
|
||||||
.child(DockLayout::tabs().panel_view(Arc::new(greeter), cx), None);
|
.child(DockLayout::tabs().panel_view(Arc::new(greeter), cx), None);
|
||||||
|
|
||||||
this.dock
|
this.dock.update(cx, |area, cx| {
|
||||||
.update(cx, |area, cx| area.set_center(center, window, cx));
|
area.set_center(center, window, cx);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
sidebar,
|
|
||||||
dock,
|
dock,
|
||||||
title_bar_chrome,
|
title_bar_chrome,
|
||||||
tasks: vec![],
|
tasks: vec![],
|
||||||
@@ -725,20 +729,7 @@ impl Render for Workspace {
|
|||||||
.on_action(cx.listener(Self::on_command))
|
.on_action(cx.listener(Self::on_command))
|
||||||
.relative()
|
.relative()
|
||||||
.size_full()
|
.size_full()
|
||||||
.child(
|
.child(self.dock.clone())
|
||||||
h_flex()
|
|
||||||
.size_full()
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.flex_shrink_0()
|
|
||||||
.h_full()
|
|
||||||
.w(SIDEBAR_WIDTH)
|
|
||||||
.border_r_1()
|
|
||||||
.border_color(cx.theme().border_variant)
|
|
||||||
.child(self.sidebar.clone()),
|
|
||||||
)
|
|
||||||
.child(self.dock.clone()),
|
|
||||||
)
|
|
||||||
// Notifications
|
// Notifications
|
||||||
.children(notification_layer)
|
.children(notification_layer)
|
||||||
// Modals
|
// Modals
|
||||||
|
|||||||
@@ -157,8 +157,11 @@ impl Render for GreeterPanel {
|
|||||||
.label("Change theme")
|
.label("Change theme")
|
||||||
.ghost()
|
.ghost()
|
||||||
.small()
|
.small()
|
||||||
.on_click(cx.listener(move |_, _, _, cx| {
|
.on_click(cx.listener(move |_, _, window, cx| {
|
||||||
cx.dispatch_action(&Command::ToggleTheme);
|
window.dispatch_action(
|
||||||
|
Box::new(Command::ToggleTheme),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
})),
|
})),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -368,14 +368,6 @@ impl Sidebar {
|
|||||||
.when(cfg!(target_os = "macos"), |this| {
|
.when(cfg!(target_os = "macos"), |this| {
|
||||||
this.pl(px(TRAFFIC_LIGHT_PADDING))
|
this.pl(px(TRAFFIC_LIGHT_PADDING))
|
||||||
})
|
})
|
||||||
.when_none(¤t_user, |this| {
|
|
||||||
this.child(
|
|
||||||
div()
|
|
||||||
.text_xs()
|
|
||||||
.text_color(cx.theme().text_muted)
|
|
||||||
.child(SharedString::from("Import your identity to continue")),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.when_some(current_user.as_ref(), |this, public_key| {
|
.when_some(current_user.as_ref(), |this, public_key| {
|
||||||
let persons = PersonRegistry::global(cx);
|
let persons = PersonRegistry::global(cx);
|
||||||
let profile = persons.read(cx).get(public_key, cx);
|
let profile = persons.read(cx).get(public_key, cx);
|
||||||
@@ -468,6 +460,18 @@ impl Panel for Sidebar {
|
|||||||
fn panel_id(&self) -> SharedString {
|
fn panel_id(&self) -> SharedString {
|
||||||
"Sidebar".into()
|
"Sidebar".into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn title(&self, _cx: &App) -> AnyElement {
|
||||||
|
SharedString::from("Sidebar").into_any_element()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn closable(&self, _cx: &App) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn zoomable(&self, _cx: &App) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventEmitter<PanelEvent> for Sidebar {}
|
impl EventEmitter<PanelEvent> for Sidebar {}
|
||||||
@@ -493,16 +497,18 @@ impl Render for Sidebar {
|
|||||||
.image_cache(retain_all("sidebar"))
|
.image_cache(retain_all("sidebar"))
|
||||||
.size_full()
|
.size_full()
|
||||||
.gap_2()
|
.gap_2()
|
||||||
|
.bg(cx.theme().surface_background)
|
||||||
|
.border_r_1()
|
||||||
|
.border_color(cx.theme().border_variant)
|
||||||
.child(self.render_user(window, cx))
|
.child(self.render_user(window, cx))
|
||||||
.child(
|
.child(
|
||||||
v_flex()
|
v_flex()
|
||||||
.px_2()
|
.px_2()
|
||||||
.py_1()
|
|
||||||
.gap_1()
|
.gap_1()
|
||||||
.child(
|
.child(
|
||||||
NavItem::new("nav-inbox", "Inbox", Icon::new(IconName::Inbox).small())
|
NavItem::new("nav-inbox", "Inbox", Icon::new(IconName::Inbox).small())
|
||||||
.on_click(|_event, _window, cx| {
|
.on_click(|_event, window, cx| {
|
||||||
cx.dispatch_action(&Command::ShowInbox)
|
window.dispatch_action(Box::new(Command::ShowInbox), cx)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
@@ -516,27 +522,27 @@ impl Render for Sidebar {
|
|||||||
})
|
})
|
||||||
.on_click({
|
.on_click({
|
||||||
let sidebar = sidebar.clone();
|
let sidebar = sidebar.clone();
|
||||||
move |_event, _window, cx| {
|
move |_event, window, cx| {
|
||||||
if let Err(error) = sidebar.update(cx, |this, cx| {
|
if let Err(error) = sidebar.update(cx, |this, cx| {
|
||||||
this.new_requests = false;
|
this.new_requests = false;
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}) {
|
}) {
|
||||||
log::error!("Failed to clear new requests: {error}");
|
log::error!("Failed to clear new requests: {error}");
|
||||||
}
|
}
|
||||||
cx.dispatch_action(&Command::ShowRequests);
|
window.dispatch_action(Box::new(Command::ShowRequests), cx);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
NavItem::new("nav-browse", "Browse", Icon::new(IconName::Compass).small())
|
NavItem::new("nav-browse", "Browse", Icon::new(IconName::Compass).small())
|
||||||
.on_click(|_event, _window, cx| {
|
.on_click(|_event, window, cx| {
|
||||||
cx.dispatch_action(&Command::ShowBrowse)
|
window.dispatch_action(Box::new(Command::ShowBrowse), cx)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
NavItem::new("nav-search", "Search", Icon::new(IconName::Search).small())
|
NavItem::new("nav-search", "Search", Icon::new(IconName::Search).small())
|
||||||
.on_click(|_event, _window, cx| {
|
.on_click(|_event, window, cx| {
|
||||||
cx.dispatch_action(&Command::ShowSearch)
|
window.dispatch_action(Box::new(Command::ShowSearch), cx)
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user