feat: add an option to enable navigation buttons
This commit is contained in:
Generated
+1
@@ -1791,6 +1791,7 @@ dependencies = [
|
|||||||
"gpui-base",
|
"gpui-base",
|
||||||
"gpui-component",
|
"gpui-component",
|
||||||
"gpui-pre",
|
"gpui-pre",
|
||||||
|
"settings",
|
||||||
"signed_ui",
|
"signed_ui",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ publish.workspace = true
|
|||||||
gpui.workspace = true
|
gpui.workspace = true
|
||||||
gpui-component.workspace = true
|
gpui-component.workspace = true
|
||||||
gpui-base.workspace = true
|
gpui-base.workspace = true
|
||||||
|
settings = { path = "../settings" }
|
||||||
signed_ui = { path = "../signed_ui" }
|
signed_ui = { path = "../signed_ui" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|||||||
@@ -19,21 +19,23 @@ use gpui_component::animation::{Lerp as _, ease_out_cubic};
|
|||||||
use gpui_component::button::{Button, ButtonVariants as _};
|
use gpui_component::button::{Button, ButtonVariants as _};
|
||||||
use gpui_component::dock::{ClosePanel, PanelControl, PanelHandle, ToggleZoom};
|
use gpui_component::dock::{ClosePanel, PanelControl, PanelHandle, ToggleZoom};
|
||||||
use gpui_component::{ActiveTheme as _, Disableable as _, IconName, Sizable as _, h_flex, v_flex};
|
use gpui_component::{ActiveTheme as _, Disableable as _, IconName, Sizable as _, h_flex, v_flex};
|
||||||
|
use settings::{SettingsStore, TabBarSettings};
|
||||||
use signed_ui::title_bar_drag_handlers;
|
use signed_ui::title_bar_drag_handlers;
|
||||||
|
|
||||||
use crate::dock_area::SkinShared;
|
use crate::dock_area::SkinShared;
|
||||||
use crate::{TAB_BAR_HEIGHT, t, window_controls};
|
use crate::{TAB_BAR_HEIGHT, t, window_controls};
|
||||||
|
|
||||||
/// A rough size for the drag preview, reported to base so a drop placeholder
|
|
||||||
/// knows where to fly in from.
|
|
||||||
///
|
|
||||||
/// The preview itself fits its content, so this is an estimate used only to
|
|
||||||
/// place the placeholder.
|
|
||||||
const DRAG_PREVIEW_SIZE: gpui::Size<gpui::Pixels> = size(px(96.), px(30.));
|
const DRAG_PREVIEW_SIZE: gpui::Size<gpui::Pixels> = size(px(96.), px(30.));
|
||||||
const COLLAPSED_TAB_BAR_SELECTOR: &str = "signed-dock-collapsed-tab-bar";
|
const COLLAPSED_TAB_BAR_SELECTOR: &str = "signed-dock-collapsed-tab-bar";
|
||||||
const ACTIVE_TAB_SELECTOR: &str = "signed-dock-active-tab";
|
const ACTIVE_TAB_SELECTOR: &str = "signed-dock-active-tab";
|
||||||
const ACTIVE_TAB_CLOSE_SELECTOR: &str = "signed-dock-active-tab-close";
|
const ACTIVE_TAB_CLOSE_SELECTOR: &str = "signed-dock-active-tab-close";
|
||||||
const TAB_HOVER_GROUP: &str = "signed-dock-tab";
|
const TAB_HOVER_GROUP: &str = "signed-dock-tab";
|
||||||
|
const NAVIGATION_BUTTONS_TAB_THRESHOLD: usize = 10;
|
||||||
|
|
||||||
|
/// Whether a group's prev/next buttons should be drawn.
|
||||||
|
fn navigation_buttons_visible(hide_navigation_buttons: bool, panels_len: usize) -> bool {
|
||||||
|
!hide_navigation_buttons && panels_len > NAVIGATION_BUTTONS_TAB_THRESHOLD
|
||||||
|
}
|
||||||
|
|
||||||
/// A panel's title, or its registered name when the panel has no handle.
|
/// A panel's title, or its registered name when the panel has no handle.
|
||||||
pub(crate) fn panel_title(
|
pub(crate) fn panel_title(
|
||||||
@@ -48,9 +50,6 @@ pub(crate) fn panel_title(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The preview that follows the cursor while a panel is dragged.
|
/// The preview that follows the cursor while a panel is dragged.
|
||||||
///
|
|
||||||
/// Base's `DragPanel` is the payload and draws nothing, this is the appearance half.
|
|
||||||
/// It fits its content rather than a fixed width, so a long title is never cut off.
|
|
||||||
struct DragPanelPreview {
|
struct DragPanelPreview {
|
||||||
panel: Arc<dyn BasePanelView>,
|
panel: Arc<dyn BasePanelView>,
|
||||||
}
|
}
|
||||||
@@ -277,15 +276,22 @@ impl SignedTabGroupSkin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The previous and next tab buttons in the tab bar's leading prefix.
|
/// The previous and next tab buttons in the tab bar's leading prefix.
|
||||||
/// Always rendered, disabled at the strip ends or when collapsed.
|
///
|
||||||
fn render_prev_next_tab_buttons(
|
/// Hidden by default, and only offered for crowded strips where the active
|
||||||
&self,
|
/// tab may have scrolled out of view. Disabled at the strip ends or collapsed.
|
||||||
group: &TabGroupContext,
|
fn render_prev_next_tab_buttons(&self, group: &TabGroupContext, cx: &mut App) -> AnyElement {
|
||||||
_cx: &mut App,
|
|
||||||
) -> impl IntoElement {
|
|
||||||
let collapsed = group.is_collapsed();
|
let collapsed = group.is_collapsed();
|
||||||
let active_ix = group.active_ix();
|
let active_ix = group.active_ix();
|
||||||
let panels_len = group.panels().len();
|
let panels_len = group.panels().len();
|
||||||
|
|
||||||
|
let hidden = SettingsStore::try_global(cx)
|
||||||
|
.map(|store| store.read(cx).settings().tab_bar.hide_navigation_buttons)
|
||||||
|
.unwrap_or_else(|| TabBarSettings::default().hide_navigation_buttons);
|
||||||
|
|
||||||
|
if !navigation_buttons_visible(hidden, panels_len) {
|
||||||
|
return Empty.into_any_element();
|
||||||
|
}
|
||||||
|
|
||||||
let prev_enabled = !collapsed && active_ix > 0;
|
let prev_enabled = !collapsed && active_ix > 0;
|
||||||
let next_enabled = !collapsed && active_ix + 1 < panels_len;
|
let next_enabled = !collapsed && active_ix + 1 < panels_len;
|
||||||
|
|
||||||
@@ -317,11 +323,10 @@ impl SignedTabGroupSkin {
|
|||||||
move |_, window, cx| group.select_tab(active_ix + 1, window, cx)
|
move |_, window, cx| group.select_tab(active_ix + 1, window, cx)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
.into_any_element()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// One tab of the pill strip.
|
/// One tab of the pill strip.
|
||||||
/// While collapsed, tabs lose the active style and all interactions.
|
|
||||||
/// The strip is also how a closed bottom dock is opened again.
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn render_tab(
|
fn render_tab(
|
||||||
&self,
|
&self,
|
||||||
@@ -848,7 +853,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
use gpui_base::dock::{DockArea, DockLayout, DockPlacement, PanelEvent};
|
use gpui_base::dock::{DockArea, DockLayout, DockPlacement, PanelEvent};
|
||||||
|
|
||||||
use super::COLLAPSED_TAB_BAR_SELECTOR;
|
use super::{COLLAPSED_TAB_BAR_SELECTOR, navigation_buttons_visible};
|
||||||
use crate::{BasePanel, Panel, SignedDockSkin, panel_handle};
|
use crate::{BasePanel, Panel, SignedDockSkin, panel_handle};
|
||||||
|
|
||||||
struct Probe {
|
struct Probe {
|
||||||
@@ -947,4 +952,15 @@ mod tests {
|
|||||||
reserved_strip,
|
reserved_strip,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn navigation_buttons_follow_the_setting_and_tab_count() {
|
||||||
|
// Hidden by default, no matter how many tabs there are.
|
||||||
|
assert!(!navigation_buttons_visible(true, 1));
|
||||||
|
assert!(!navigation_buttons_visible(true, 20));
|
||||||
|
|
||||||
|
// When not hidden they still only appear for a crowded strip.
|
||||||
|
assert!(!navigation_buttons_visible(false, 10));
|
||||||
|
assert!(navigation_buttons_visible(false, 11));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,6 +54,22 @@ impl Default for ThemeSettings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tab bar chrome.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
#[serde(default)]
|
||||||
|
pub struct TabBarSettings {
|
||||||
|
/// Hide the previous and next tab buttons.
|
||||||
|
pub hide_navigation_buttons: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TabBarSettings {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
hide_navigation_buttons: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct GraspServersSettings {
|
pub struct GraspServersSettings {
|
||||||
@@ -126,6 +142,7 @@ pub struct CreateRepositorySettings {
|
|||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
pub appearance: AppearanceMode,
|
pub appearance: AppearanceMode,
|
||||||
pub theme: ThemeSettings,
|
pub theme: ThemeSettings,
|
||||||
|
pub tab_bar: TabBarSettings,
|
||||||
pub grasp_servers: GraspServersSettings,
|
pub grasp_servers: GraspServersSettings,
|
||||||
pub local_repos: LocalReposSettings,
|
pub local_repos: LocalReposSettings,
|
||||||
pub checkouts: CheckoutsSettings,
|
pub checkouts: CheckoutsSettings,
|
||||||
|
|||||||
@@ -24,6 +24,12 @@ impl SettingsStore {
|
|||||||
cx.global::<GlobalSettingsStore>().0.clone()
|
cx.global::<GlobalSettingsStore>().0.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieve the global settings store if one has been installed.
|
||||||
|
pub fn try_global(cx: &App) -> Option<Entity<Self>> {
|
||||||
|
cx.try_global::<GlobalSettingsStore>()
|
||||||
|
.map(|store| store.0.clone())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_global(entity: Entity<Self>, cx: &mut App) {
|
pub fn set_global(entity: Entity<Self>, cx: &mut App) {
|
||||||
cx.set_global(GlobalSettingsStore(entity));
|
cx.set_global(GlobalSettingsStore(entity));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -290,6 +290,8 @@ fn settings_view(controls: &SettingsControls, cx: &mut App) -> impl IntoElement
|
|||||||
.child(Separator::horizontal())
|
.child(Separator::horizontal())
|
||||||
.child(theme_section(&settings, controls, cx))
|
.child(theme_section(&settings, controls, cx))
|
||||||
.child(Separator::horizontal())
|
.child(Separator::horizontal())
|
||||||
|
.child(tab_bar_section(&settings, cx))
|
||||||
|
.child(Separator::horizontal())
|
||||||
.child(grasp_servers_section(&settings, controls, cx))
|
.child(grasp_servers_section(&settings, controls, cx))
|
||||||
.child(Separator::horizontal())
|
.child(Separator::horizontal())
|
||||||
.child(repositories_section(&settings, controls, cx))
|
.child(repositories_section(&settings, controls, cx))
|
||||||
@@ -374,6 +376,26 @@ fn theme_section(settings: &Settings, controls: &SettingsControls, cx: &App) ->
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tab bar chrome, such as its next/previous tab buttons.
|
||||||
|
fn tab_bar_section(settings: &Settings, cx: &App) -> impl IntoElement {
|
||||||
|
v_flex().w_full().gap_3().child(setting_row(
|
||||||
|
cx,
|
||||||
|
"Hide Tab Navigation Buttons",
|
||||||
|
"Enable to show navigation buttons when more than 10 tabs.",
|
||||||
|
Switch::new("hide-tab-navigation")
|
||||||
|
.checked(settings.tab_bar.hide_navigation_buttons)
|
||||||
|
.on_click(move |checked: &bool, _window, cx| {
|
||||||
|
let store = SettingsStore::global(cx);
|
||||||
|
store.update(cx, |store, cx| {
|
||||||
|
store.edit(
|
||||||
|
|settings| settings.tab_bar.hide_navigation_buttons = *checked,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
/// Default grasp servers, used until the user's kind `10317` grasp list loads.
|
/// Default grasp servers, used until the user's kind `10317` grasp list loads.
|
||||||
fn grasp_servers_section(
|
fn grasp_servers_section(
|
||||||
settings: &Settings,
|
settings: &Settings,
|
||||||
|
|||||||
Reference in New Issue
Block a user