Skip to content

Commit 9a4f60d

Browse files
committed
feat: scroll to active tab when tab is not visible
1 parent 83b8f44 commit 9a4f60d

File tree

3 files changed

+325
-48
lines changed

3 files changed

+325
-48
lines changed

src/extensionsIntegrated/TabBar/main.js

Lines changed: 130 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ define(function (require, exports, module) {
88
const CommandManager = require("command/CommandManager");
99
const Commands = require("command/Commands");
1010
const DocumentManager = require("document/DocumentManager");
11+
const WorkspaceManager = require("view/WorkspaceManager");
1112

1213
const Global = require("./global");
1314
const Helper = require("./helper");
@@ -141,7 +142,7 @@ define(function (require, exports, module) {
141142
* Sets up the tab bar
142143
*/
143144
function setupTabBar() {
144-
// this populates the working sets
145+
// this populates the working sets present in `global.js`
145146
getAllFilesFromWorkingSet();
146147

147148
// if no files are present in a pane, we want to hide the tab bar for that pane
@@ -190,6 +191,7 @@ define(function (require, exports, module) {
190191
displayedEntries.forEach(function (entry) {
191192
$firstTabBar.append(createTab(entry));
192193
Overflow.toggleOverflowVisibility("first-pane");
194+
Overflow.scrollToActiveTab($firstTabBar);
193195
});
194196
}
195197

@@ -207,6 +209,7 @@ define(function (require, exports, module) {
207209
displayedEntries2.forEach(function (entry) {
208210
$secondTabBar.append(createTab(entry));
209211
Overflow.toggleOverflowVisibility("second-pane");
212+
Overflow.scrollToActiveTab($firstTabBar);
210213
});
211214
}
212215
}
@@ -225,21 +228,129 @@ define(function (require, exports, module) {
225228

226229
if ($('.not-editor').length === 1) {
227230
$tabBar = $(TabBarHTML);
228-
// since we need to add the tab bar before the editor area, we target the `.not-editor` class
229-
$(".not-editor").before($tabBar);
231+
// since we need to add the tab bar before the editor area, we target the `#editor-holder` class and prepend
232+
$("#editor-holder").prepend($tabBar);
233+
setTimeout(function () {
234+
WorkspaceManager.recomputeLayout(true);
235+
}, 0);
236+
230237
} else if ($('.not-editor').length === 2) {
231238
$tabBar = $(TabBarHTML);
232239
$tabBar2 = $(TabBarHTML2);
233240

234241
// eq(0) is for the first pane and eq(1) is for the second pane
242+
// here #editor-holder cannot be used as in split view, we only have one #editor-holder
243+
// so, right now we are using .not-editor. Maybe we need to look for some better selector
244+
// TODO: Fix bug where the tab bar gets hidden inside the editor in horizontal split
235245
$(".not-editor").eq(0).before($tabBar);
236246
$(".not-editor").eq(1).before($tabBar2);
247+
setTimeout(function () {
248+
WorkspaceManager.recomputeLayout(true);
249+
}, 0);
237250
}
238251

239252
setupTabBar();
240253
}
241254

242255

256+
/**
257+
* This function updates the tabs in the tab bar
258+
* It is called when the working set changes. So instead of creating a new tab bar, we just update the existing one
259+
*/
260+
function updateTabs() {
261+
// Get all files from the working set. refer to `global.js`
262+
getAllFilesFromWorkingSet();
263+
264+
// When there is only one file, we enforce the creation of the tab bar
265+
// this is done because, given the situation:
266+
// In a vertical split, when no files are present in 'second-pane' so the tab bar is hidden.
267+
// Now, when the user adds a file in 'second-pane', the tab bar should be shown but since updateTabs() only,
268+
// updates the tabs, so the tab bar never gets created.
269+
if (Global.firstPaneWorkingSet.length === 1 || Global.secondPaneWorkingSet.length === 1) {
270+
createTabBar();
271+
return;
272+
}
273+
274+
const $firstTabBar = $('#phoenix-tab-bar');
275+
// Update first pane's tabs
276+
if ($firstTabBar.length) {
277+
$firstTabBar.empty();
278+
if (Global.firstPaneWorkingSet.length > 0) {
279+
280+
// get the count of tabs that we want to display in the tab bar (from preference settings)
281+
// from preference settings or working set whichever smaller
282+
let tabsCountP1 = Math.min(Global.firstPaneWorkingSet.length, Preference.tabBarNumberOfTabs);
283+
284+
// the value is generally '-1', but we check for less than 0 so that it can handle edge cases gracefully
285+
// if the value is negative then we display all tabs
286+
if (Preference.tabBarNumberOfTabs < 0) {
287+
tabsCountP1 = Global.firstPaneWorkingSet.length;
288+
}
289+
290+
let displayedEntries = Global.firstPaneWorkingSet.slice(0, tabsCountP1);
291+
292+
const activeEditor = EditorManager.getActiveEditor();
293+
const activePath = activeEditor ? activeEditor.document.file.fullPath : null;
294+
if (activePath && !displayedEntries.some(entry => entry.path === activePath)) {
295+
let activeEntry = Global.firstPaneWorkingSet.find(entry => entry.path === activePath);
296+
if (activeEntry) {
297+
displayedEntries[displayedEntries.length - 1] = activeEntry;
298+
}
299+
}
300+
displayedEntries.forEach(function (entry) {
301+
$firstTabBar.append(createTab(entry));
302+
});
303+
}
304+
}
305+
306+
const $secondTabBar = $('#phoenix-tab-bar-2');
307+
// Update second pane's tabs
308+
if ($secondTabBar.length) {
309+
$secondTabBar.empty();
310+
if (Global.secondPaneWorkingSet.length > 0) {
311+
312+
let tabsCountP2 = Math.min(Global.secondPaneWorkingSet.length, Preference.tabBarNumberOfTabs);
313+
if (Preference.tabBarNumberOfTabs < 0) {
314+
tabsCountP2 = Global.secondPaneWorkingSet.length;
315+
}
316+
317+
let displayedEntries2 = Global.secondPaneWorkingSet.slice(0, tabsCountP2);
318+
const activeEditor = EditorManager.getActiveEditor();
319+
const activePath = activeEditor ? activeEditor.document.file.fullPath : null;
320+
if (activePath && !displayedEntries2.some(entry => entry.path === activePath)) {
321+
let activeEntry = Global.secondPaneWorkingSet.find(entry => entry.path === activePath);
322+
if (activeEntry) {
323+
displayedEntries2[displayedEntries2.length - 1] = activeEntry;
324+
}
325+
}
326+
displayedEntries2.forEach(function (entry) {
327+
$secondTabBar.append(createTab(entry));
328+
});
329+
}
330+
}
331+
332+
// if no files are present in a pane, we want to hide the tab bar for that pane
333+
if (Global.firstPaneWorkingSet.length === 0 && ($('#phoenix-tab-bar'))) {
334+
Helper._hideTabBar($('#phoenix-tab-bar'), $('#overflow-button'));
335+
}
336+
337+
if (Global.secondPaneWorkingSet.length === 0 && ($('#phoenix-tab-bar-2'))) {
338+
Helper._hideTabBar($('#phoenix-tab-bar-2'), $('#overflow-button-2'));
339+
}
340+
341+
// Now that tabs are updated, scroll to the active tab if necessary.
342+
if ($firstTabBar.length) {
343+
Overflow.toggleOverflowVisibility("first-pane");
344+
Overflow.scrollToActiveTab($firstTabBar);
345+
}
346+
347+
if ($secondTabBar.length) {
348+
Overflow.toggleOverflowVisibility("second-pane");
349+
Overflow.scrollToActiveTab($secondTabBar);
350+
}
351+
}
352+
353+
243354
/**
244355
* Removes existing tab bar and cleans up
245356
*/
@@ -358,29 +469,25 @@ define(function (require, exports, module) {
358469
* Registers the event handlers
359470
*/
360471
function registerHandlers() {
361-
362-
// pane handlers
472+
// For pane changes, still recreate the entire tab bar container.
363473
MainViewManager.off("activePaneChange paneCreate paneDestroy paneLayoutChange", createTabBar);
364474
MainViewManager.on("activePaneChange paneCreate paneDestroy paneLayoutChange", createTabBar);
365475

366-
// editor handlers
367-
EditorManager.off("activeEditorChange", createTabBar);
368-
EditorManager.on("activeEditorChange", createTabBar);
369-
370-
// when working set changes, recreate the tab bar
371-
// we listen to all of these events to ensure that the tab bar is always up to date
372-
// refer to `MainViewManager.js` for more details
373-
MainViewManager.on("workingSetAdd", createTabBar);
374-
MainViewManager.on("workingSetRemove", createTabBar);
375-
MainViewManager.on("workingSetSort", createTabBar);
376-
MainViewManager.on("workingSetMove", createTabBar);
377-
MainViewManager.on("workingSetAddList", createTabBar);
378-
MainViewManager.on("workingSetRemoveList", createTabBar);
379-
MainViewManager.on("workingSetUpdate", createTabBar);
380-
MainViewManager.on("_workingSetDisableAutoSort", createTabBar);
381-
382-
383-
// file dirty flag change handler
476+
// For editor changes, update only the tabs.
477+
EditorManager.off("activeEditorChange", updateTabs);
478+
EditorManager.on("activeEditorChange", updateTabs);
479+
480+
// For working set changes, update only the tabs.
481+
MainViewManager.on("workingSetAdd", updateTabs);
482+
MainViewManager.on("workingSetRemove", updateTabs);
483+
MainViewManager.on("workingSetSort", updateTabs);
484+
MainViewManager.on("workingSetMove", updateTabs);
485+
MainViewManager.on("workingSetAddList", updateTabs);
486+
MainViewManager.on("workingSetRemoveList", updateTabs);
487+
MainViewManager.on("workingSetUpdate", updateTabs);
488+
MainViewManager.on("_workingSetDisableAutoSort", updateTabs);
489+
490+
// file dirty flag change remains unchanged.
384491
DocumentManager.on("dirtyFlagChange", function (event, doc) {
385492
const filePath = doc.file.fullPath;
386493

0 commit comments

Comments
 (0)