Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions src/_locales/dict.setup-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3623,6 +3623,67 @@ Available parameters:
zh_TW: '無',
ja: 'なし',
},
'settings.sticky_active_tab': {
en: 'Sticky active tab',
},
'settings.sticky_ancestor_tabs': {
en: 'Sticky ancestor tabs',
de: 'Fixierte übergeordnete Tabs',
fr: 'Onglets ancêtres fixes',
hu: 'Rögzített felmenő lapok',
pl: 'Przypięte karty nadrzędne',
// ru: 'Закреплённые вкладки-предки', // TODO: Закреплённые already used for pinned
zh_CN: '固定祖先标签页',
zh_TW: '固定祖先分頁',
ja: '先祖タブを固定表示',
},
'settings.sticky_ancestor_tabs_limit': {
en: 'Depth limit',
// TODO: Updated, need translation
// de: 'Maximale Tiefe der fixierten übergeordneten Tabs',
// fr: 'Limite de profondeur des onglets ancêtres fixes',
// hu: 'A rögzített felmenő lapok mélységkorlátja',
// pl: 'Limit głębokości przypiętych kart nadrzędnych',
// ru: 'Максимальная глубина закреплённых вкладок-предков',
// zh_CN: '固定祖先标签页深度上限',
// zh_TW: '固定祖先分頁深度上限',
// ja: '固定する先祖タブの深さ制限',
},
'settings.sticky_ancestor_tabs_limit_1': {
en: '1',
},
'settings.sticky_ancestor_tabs_limit_2': {
en: '2',
},
'settings.sticky_ancestor_tabs_limit_3': {
en: '3',
},
'settings.sticky_ancestor_tabs_limit_4': {
en: '4',
},
'settings.sticky_ancestor_tabs_limit_5': {
en: '5',
},
'settings.sticky_ancestor_tabs_limit_none': {
en: 'none',
de: 'Unbegrenzt',
fr: 'aucun',
hu: 'nincs',
pl: 'brak',
ru: 'выкл',
zh_CN: '无',
zh_TW: '無',
ja: 'なし',
},
'settings.sticky_ancestor_tabs_layout': {
en: 'Layout',
},
'settings.sticky_ancestor_tabs_layout_col': {
en: 'vertical',
},
'settings.sticky_ancestor_tabs_layout_row': {
en: 'horizontal',
},
'settings.auto_fold_tabs': {
en: 'Auto fold tabs branch on expanding another branch',
de: 'Zweig beim Öffnen eines anderen Zweiges automatisch einklappen',
Expand Down
3 changes: 3 additions & 0 deletions src/defaults/panels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export const TABS_PANEL_STATE: T.TabsPanel = {
nextTabIndex: -1,
scrollRetainer: 0,
allDiscarded: false,
ntbbEl: null,

class: E.NavItemClass.panel,
index: -1,
Expand Down Expand Up @@ -115,6 +116,8 @@ export const TABS_PANEL_STATE: T.TabsPanel = {
visibleTabIds: [],
pinnedTabIds: [],
badge: false,
stickyTabIdsTop: [],
stickyTabIdsBottom: [],
selNewTab: false,
scrollRetainerHeight: 0,
empty: true,
Expand Down
6 changes: 6 additions & 0 deletions src/defaults/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@ export const DEFAULT_SETTINGS: SettingsState = {
// Tabs tree
tabsTree: true,
tabsTreeLimit: 'none',
stickyActiveTab: false,
stickyAncestorTabs: false,
stickyAncestorTabsLimit: 3,
stickyAncestorTabsLayout: 'col',
autoFoldTabs: false,
autoFoldTabsExcept: 'none',
autoExpandTabs: false,
Expand Down Expand Up @@ -336,6 +340,8 @@ export const SETTINGS_OPTIONS = {
tabsUpdateMark: ['all', 'pin', 'norm', 'none'], // DEPR
pinnedTabsPosition: ['panel', 'top', 'left', 'right'],
tabsTreeLimit: [1, 2, 3, 4, 5, 'none'],
stickyAncestorTabsLimit: [1, 2, 3, 4, 5, 'none'],
stickyAncestorTabsLayout: ['col', 'row'],
previewTabsMode: ['i', 'p'],
previewTabsPageModeFallback: ['i', 'n'],
previewTabsSide: ['right', 'left'],
Expand Down
33 changes: 33 additions & 0 deletions src/page.setup/components/settings.tabs.vue
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,39 @@ section(ref="el")
:inactive="!Settings.state.tabsTree"
:opts="Settings.getOpts('tabsTreeLimit')"
@update:value="Settings.saveDebounced(150)")
ToggleField(
label="settings.sticky_active_tab"
dbg="stickyActiveTab"
v-model:value="Settings.state.stickyActiveTab"
:default="DEFAULT_SETTINGS.stickyActiveTab"
:inactive="!Settings.state.tabsTree"
@update:value="Settings.saveDebounced(150)")
ToggleField(
label="settings.sticky_ancestor_tabs"
dbg="stickyAncestorTabs"
v-model:value="Settings.state.stickyAncestorTabs"
:default="DEFAULT_SETTINGS.stickyAncestorTabs"
:inactive="!Settings.state.tabsTree"
@update:value="Settings.saveDebounced(150)")
.sub-fields
SelectField(
label="settings.sticky_ancestor_tabs_limit"
optLabel="settings.sticky_ancestor_tabs_limit_"
dbg="stickyAncestorTabsLimit"
v-model:value="Settings.state.stickyAncestorTabsLimit"
:default="DEFAULT_SETTINGS.stickyAncestorTabsLimit"
:inactive="!Settings.state.tabsTree || !Settings.state.stickyAncestorTabs"
:opts="Settings.getOpts('stickyAncestorTabsLimit')"
@update:value="Settings.saveDebounced(150)")
SelectField(
label="settings.sticky_ancestor_tabs_layout"
optLabel="settings.sticky_ancestor_tabs_layout_"
dbg="stickyAncestorTabsLayout"
v-model:value="Settings.state.stickyAncestorTabsLayout"
:default="DEFAULT_SETTINGS.stickyAncestorTabsLayout"
:inactive="!Settings.state.tabsTree || !Settings.state.stickyAncestorTabs"
:opts="Settings.getOpts('stickyAncestorTabsLayout')"
@update:value="Settings.saveDebounced(150)")
ToggleField(
label="settings.auto_fold_tabs"
:inactive="!Settings.state.tabsTree"
Expand Down
10 changes: 10 additions & 0 deletions src/services/settings.fg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,9 @@ export function updateSettings(settings?: SettingsState | null): void {
const markWindowPreface = prev.markWindowPreface !== next.markWindowPreface
const tabsUnreadMark = prev.tabsUnreadMark !== next.tabsUnreadMark
const copyTemplates = prev.copyTemplates !== next.copyTemplates
const stickyAncestorTabs = prev.stickyAncestorTabs !== next.stickyAncestorTabs
const stickyAncestorTabsLimit = prev.stickyAncestorTabsLimit !== next.stickyAncestorTabsLimit
const stickyAncestorTabsLayout = prev.stickyAncestorTabsLayout !== next.stickyAncestorTabsLayout

// Update settings of this instance
Utils.updateObject(Settings.state, settings, Settings.state)
Expand Down Expand Up @@ -229,6 +232,13 @@ export function updateSettings(settings?: SettingsState | null): void {
if (Info.isSidebar && copyTemplates) Settings.parseCopyTemplates()

Search.parseShortcuts()

if (stickyAncestorTabs || stickyAncestorTabsLimit || stickyAncestorTabsLayout) {
const actPanel = Sidebar.panelsById[Sidebar.activePanelId]
if (Utils.isTabsPanel(actPanel)) {
Tabs.calcStickyTabs(actPanel)
}
}
}

export function resetSettings(): void {
Expand Down
17 changes: 17 additions & 0 deletions src/services/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ export let updateWinPrefaceOnPanelSwitch = false
export let initSaveNeeded = false
export let copyTemplates: CopyTemplate[] = []

export let newTabBarPositionAfterTabs = false
export let newTabBarPositionBottom = false

export let rmChildTabsFolded = false
export let rmChildTabsAll = false
export let rmChildTabsNone = false
Expand All @@ -19,6 +22,11 @@ export let activateAfterClosingNext = false
export let activateAfterClosingPrev = false
export let activateAfterClosingPrevAct = false

export let stickyTabs = false
export let stickyAncestorTabsLimit = 16
export let stickyAncestorTabsLayoutCol = false
export let stickyAncestorTabsLayoutRow = false

export function reactivate(r: Reactivator<SettingsState>) {
state = r(state)
}
Expand Down Expand Up @@ -100,6 +108,9 @@ export async function load(): Promise<void> {
}

export function updPrecalcSettings() {
newTabBarPositionAfterTabs = state.showNewTabBtns && state.newTabBarPosition === 'after_tabs'
newTabBarPositionBottom = state.showNewTabBtns && state.newTabBarPosition === 'bottom'

rmChildTabsFolded = state.rmChildTabs === 'folded'
rmChildTabsAll = state.rmChildTabs === 'all'
rmChildTabsNone = state.rmChildTabs === 'none'
Expand All @@ -108,6 +119,12 @@ export function updPrecalcSettings() {
activateAfterClosingNext = state.activateAfterClosing === 'next'
activateAfterClosingPrev = state.activateAfterClosing === 'prev'
activateAfterClosingPrevAct = state.activateAfterClosing === 'prev_act'

stickyTabs = state.stickyActiveTab || state.stickyAncestorTabs
stickyAncestorTabsLimit =
typeof state.stickyAncestorTabsLimit === 'number' ? state.stickyAncestorTabsLimit : 16
stickyAncestorTabsLayoutCol = state.stickyAncestorTabsLayout === 'col'
stickyAncestorTabsLayoutRow = state.stickyAncestorTabsLayout === 'row'
}

export function resetSettings(): void {
Expand Down
6 changes: 6 additions & 0 deletions src/services/sidebar.fg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,8 @@ function recalcVisibleTabsInPanel(panelId: ID) {
}
panel.reactive.visibleTabIds = visibleTabIds
}

if (activePanelId === panelId) requestAnimationFrame(() => Tabs.calcStickyTabs(panel))
}

export function addToVisibleTabs(panelId: ID, tab: T.Tab) {
Expand All @@ -536,6 +538,8 @@ export function addToVisibleTabs(panelId: ID, tab: T.Tab) {
if (index === -1) return recalcVisibleTabsInPanel(panelId)

panel.reactive.visibleTabIds.splice(index - invisibleShift, 0, tabId)

if (activePanelId === panelId) requestAnimationFrame(() => Tabs.calcStickyTabs(panel))
}

export function removeFromVisibleTabs(panelId: ID, tabId: ID) {
Expand All @@ -545,6 +549,8 @@ export function removeFromVisibleTabs(panelId: ID, tabId: ID) {
const visibleTabIds = panel.reactive.visibleTabIds
const index = visibleTabIds.indexOf(tabId)
if (index !== -1) visibleTabIds.splice(index, 1)

if (activePanelId === panelId) requestAnimationFrame(() => Tabs.calcStickyTabs(panel))
}

const checkDiscardedTabsInPanelTimeouts = new Map<ID, number>()
Expand Down
9 changes: 9 additions & 0 deletions src/services/tabs.fg.handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1732,6 +1732,15 @@ function onTabActivated(info: browser.tabs.ActiveInfo): void {
const panel = Sidebar.panelsById[tab.panelId]
if (!Utils.isTabsPanel(panel)) return

// Update sticky tabs
if (Settings.stickyTabs) {
Tabs.calcStickyTabs(panel)
if (prevActive && prevActive.panelId !== panel.id) {
const prevPanel = Sidebar.panelsById[prevActive.panelId]
if (Utils.isTabsPanel(prevPanel)) Tabs.calcStickyTabs(prevPanel)
}
}

// Update succession
Tabs.updateSuccessionDebounced(0)

Expand Down
7 changes: 5 additions & 2 deletions src/services/tabs.fg.preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export const state = {
modeFallback: false,

targetTabId: NOID,
sticky: false,

openTimeout: undefined as number | undefined,
closeTimeout: undefined as number | undefined,
Expand Down Expand Up @@ -61,14 +62,15 @@ function dbgStr() {
return `mode: ${m}, status: ${s}, targetTabId: ${state.targetTabId}, doa: ${deadOnArrival}`
}

export function setTargetTab(tabId: ID) {
export function setTargetTab(tabId: ID, sticky: boolean) {
clearTimeout(state.openTimeout)
if (Settings.state.previewTabsFollowMouse) {
clearTimeout(state.closeTimeout)
}

const tab = Tabs.byId[tabId]
state.targetTabId = tabId
state.sticky = sticky

// Start timeout to...
if (!Menu.isOpen && !Mouse.multiSelectionMode && !Selection.selected.size && tab) {
Expand Down Expand Up @@ -130,6 +132,7 @@ export function resetTargetTab(tabId: ID, closeDelay = DEFERRED_CLOSE_DELAY) {
state.targetTabId = NOID
}

state.sticky = false
state.openTimeout = undefined

state.closeTimeout = setTimeout(() => {
Expand Down Expand Up @@ -401,7 +404,7 @@ async function updateSPreview(tabId: ID) {
}

function setSPreviewPosition(popupEl: HTMLElement, tab: Tab) {
const el = document.getElementById(`tab${tab.id}`)
const el = document.getElementById(state.sticky ? `stickytab${tab.id}` : `tab${tab.id}`)
if (!el) return

const tb = el.getBoundingClientRect()
Expand Down
Loading
Loading