Skip to content

Commit

Permalink
Manually filter by pressing 'f', clear all active filters by pressing…
Browse files Browse the repository at this point in the history
… 'd', limit search to filtered tabs, more context menu options on tabs for discarding and reloading, and other additions and fixes

- New keyboard shortcuts
-- Manual filtering by pressing 'f'
-- Clear all filters, including manual filters and the search filter by pressing 'd'
- If manual filtering is active, only search within filtered tabs
- Increase the height of the preview placeholder for unloaded tabs
- New attribute `data-discarded` on tab entries indicating if tab has been discarded
- Adjusted menu item appearances
- `TUIList` changes
-- New method `isElementHiddenBy` for checking if an element is currently being hidden by a reason
-- `hideElement` and `showElement` now accept `includeSub` as a parameter
-- New method `getSelected` for obtaining an array of elements currently being multiselected
- Move tabs in one go when their positions change instead of removing them first and then adding them back in; fixes issue where active tabs being moved across windows have their "active" events missed due to them being removed temporarily
- New context menu options on tabs for loading and unloading them
- New methods `reload` and `discard` on `TTabActions` for reloading and discarding sets of tabs accordingly
- Fixed tabs list going off-screen when the search bar grows vertically
  • Loading branch information
Bill13579 committed Sep 8, 2024
1 parent 7357108 commit 23cba20
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 17 deletions.
1 change: 1 addition & 0 deletions dist/icons/refresh.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
48 changes: 47 additions & 1 deletion dist/popup/popup.css
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,10 @@ html, body {
#details-pane::-webkit-scrollbar {
display: none; /* Safari and Chrome */
}
#session-display-pane {
display: flex;
flex-direction: column;
}
#session-pane, #session-display-pane {
box-sizing: border-box;
overflow: hidden;
Expand Down Expand Up @@ -371,7 +375,7 @@ html, body {
#details-pane #unloaded-tab-preview-placeholder {
display: none;
width: 100%;
height: calc(150px * var(--scale));
height: calc(220px * var(--scale));
color: var(--fg-label);
justify-content: center;
}
Expand Down Expand Up @@ -576,6 +580,35 @@ html, body {
background-image: none;
}

/* Filter indicator layout */
/* #session-display-pane #session-top-pane .manual-filter-indicator-label {
grid-row: 3;
grid-column: 3 span;
align-self: end;
justify-self: end;
font-weight: bold;
color: var(--highlight-1);
} */
.manual-filter-indicator {
position: absolute;
border-radius: 4px;
box-shadow: 0 0 0 4px var(--highlight-1);
animation-name: pop;
animation-duration: .5s;
pointer-events: none;
background: #ff835426;
z-index: 10000;
}
.manual-filter-indicator[data-inactive] {
display: none;
}
@keyframes pop {
0% { box-shadow: 0 0 0 4px var(--highlight-1); }
3% { box-shadow: 0 0 0 5px var(--highlight-1); }
10% { box-shadow: 0 0 0 6px var(--highlight-1); }
100% { box-shadow: 0 0 0 4px var(--highlight-1); }
}

/* Tab list layout (huge chunk of css incoming) */

#where-to-put-the-tab-list {
Expand All @@ -599,6 +632,9 @@ html, body {
.tab-list .tab-entry.-tui-list-hidden--filter {
display: none;
}
.tab-list .tab-entry.-tui-list-hidden--manual-filter {
display: none;
}
.tab-list .tab-entry.-tui-list-selected .selected-indicator {
background: linear-gradient(90deg, var(--selected) 0%, var(--selected) 80%, var(--bg-indented) 80%, #ffffff00 100%);
height: calc(100% + 4px);
Expand Down Expand Up @@ -664,6 +700,9 @@ html, body {
border-style: dashed;
}

.tab-list .window-entry:first-child {
margin-top: 0px;
}
.tab-list .window-entry {
display: -ms-flex;
display: -webkit-flex;
Expand Down Expand Up @@ -802,6 +841,13 @@ html, body {
background-color: var(--current-tab90);
}

.tab-list .tab-entry[data-discarded] {
/* CSS rule that can be used to style discarded tabs differently. */
/* height: calc(13px * var(--scale)); */
/* background-color: var(--bg-indented-translucent); */
/* opacity: 0.9; */
}

.tab-list .tab-entry.-tui-list-hover,
.tab-list .tab-entry.-tui-list-hover.incognito {
border-color: var(--fg-label);
Expand Down
2 changes: 2 additions & 0 deletions dist/stylesheets/theming.css
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
--selected: rgb(48, 97, 255);
--btn-inline-text-hover: #0062ff;
--selected-2: rgb(37, 81, 224);
--highlight-1: #ff9500;

--input-focus90: #e791ec; /*currently unused*/
--input-focus90t: #ea4af260; /*currently unused*/
Expand Down Expand Up @@ -66,6 +67,7 @@
--selected: #dce9ff;
--btn-inline-text-hover: #0062ff;
--selected-2: #dce9ff;
--highlight-1: #ff9500;

--input-focus90: #e791ec; /*currently unused*/
--input-focus90t: #ea4af260; /*currently unused*/
Expand Down
7 changes: 7 additions & 0 deletions src/polyfill.js
Original file line number Diff line number Diff line change
Expand Up @@ -305,3 +305,10 @@ globalScope["t_getKeyboardFocusableElements"] = (element = document) => {
)
}

// https://stackoverflow.com/questions/6268508/restart-animation-in-css3-any-better-way-than-removing-the-element
globalScope["t_resetAnimation"] = function (el) {
el.style.animation = 'none';
el.offsetHeight; /* trigger reflow */
el.style.animation = null;
};

12 changes: 5 additions & 7 deletions src/popup/menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ export class TUIMenu extends TUIMenuListLayout {
}
}
export class TUIMenuItem {
constructor(label="Label", icon="", iconTransform="scale(90%) translateY(-4%)", markdown=false, data=undefined) {
constructor(label="Label", icon="", iconTransform="scale(80%) translateY(-2.8%)", markdown=false, data=undefined) {
this.__labelText = label;
this.__iconSrc = icon;
this.iconTransform = iconTransform;
Expand Down Expand Up @@ -175,7 +175,7 @@ export class TUIMenuItem {
}
set iconSrc(v) {
this.__iconSrc = v;
this.__icon.style.backgroundImage = `url(${this.__iconSrc})`;
this.__icon.style.backgroundColor = `black`;
this.__icon.style.webkitMaskImage = `url(${this.__iconSrc})`;
this.__icon.style.maskImage = `url(${this.__iconSrc})`;
}
Expand All @@ -197,7 +197,7 @@ export class TUIMenuItem {
label.classList.add("-tui-menu-item-label");

this.__setLabelContent(label, this.__labelText, this.__markdown);
icon.style.backgroundImage = `url(${this.__iconSrc})`;
icon.style.backgroundColor = "black";
icon.style.webkitMaskImage = `url(${this.__iconSrc})`;
icon.style.maskImage = `url(${this.__iconSrc})`;
icon.style.transform = this.iconTransform;
Expand Down Expand Up @@ -234,17 +234,15 @@ export class TUIMenuItem {
}
static colorIcon(icon, color) {
icon.style.backgroundColor = color;
icon.style.backgroundImage = "none";
icon.classList.remove("-force-filter-svg-to-match-theme");
}
static matchThemeIcon(icon) {
icon.style.backgroundColor = "";
icon.style.backgroundImage = "";
icon.classList.add("-force-filter-svg-to-match-theme");
}
}
export class TUIMenuLabel extends TUIMenuItem {
constructor(label="Label", icon="", iconTransform="scale(90%) translateY(-4%)", markdown=false, data=undefined) {
constructor(label="Label", icon="", iconTransform="scale(80%) translateY(-2.8%)", markdown=false, data=undefined) {
super(label, icon, iconTransform, markdown, data);
}
make(ret) {
Expand All @@ -269,7 +267,7 @@ export class TUIMenuHR {
}
}
export class TUISubMenu extends TUIMenuItem {
constructor(onMake, onSelect, options, dropdown=false, label="Label", icon="", iconTransform="scale(90%) translateY(-4%)", markdown=false, data=undefined) {
constructor(onMake, onSelect, options, dropdown=false, label="Label", icon="", iconTransform="scale(80%) translateY(-2.8%)", markdown=false, data=undefined) {
super(label, icon, iconTransform, markdown, data);
this.__onMake = onMake;
this.__onSelect = onSelect;
Expand Down
83 changes: 74 additions & 9 deletions src/popup/popup.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,20 +199,26 @@ class TUIList {
static levelOf(ele) {
return parseInt(ele.getAttribute("data-level"));
}
hideElement(ele, reason) {
for (let e of [ele, ...this.children(ele, true)]) {//TODO3
isElementHiddenBy(ele, reason) {
return ele.classList.contains("-tui-list-hidden--" + reason);
}
hideElement(ele, reason, includeSub=true) {
for (let e of [ele, ...this.children(ele, includeSub)]) {//TODO3
const event = new Event("-tui-list-hidden");
e.dispatchEvent(event);
e.classList.add("-tui-list-hidden--" + reason);
}
}
showElement(ele, reason) {
for (let e of [ele, ...this.children(ele, true)]) {//TODO3
showElement(ele, reason, includeSub=true) {
for (let e of [ele, ...this.children(ele, includeSub)]) {//TODO3
const event = new Event("-tui-list-shown");
e.dispatchEvent(event);
e.classList.remove("-tui-list-hidden--" + reason);
}
}
getSelected() {
return Array.from(this.root.getElementsByClassName("-tui-list-selected"));
}
modifySelected(callback) {
// Setup a fake dragging environment
let multiselectDragging = this.multiselectDragging;
Expand Down Expand Up @@ -940,10 +946,11 @@ class TUISessionView extends TUIListView {

if (prop === "#position" && hookPos) {
let after = list.root.querySelector(`.window-entry[data-window-id="${value.newWindowId}"]`);
after.parentElement.removeChild(e);
// after.parentElement.removeChild(e); // insertBefore will automatically move the element, so no need to manually remove here. However, this also means that the original tab will interfere with the following calculations, which will take that into account.
let i = value.newPosition;
while (i > 0) {
after = after.nextElementSibling;//TODO3
if (after && after.isEqualNode(e)) after = after.nextElementSibling; // Deal with the aforementioned self-interference.
i--;
}
after.parentElement.insertBefore(e, after.nextElementSibling);//TODO3
Expand Down Expand Up @@ -976,6 +983,9 @@ class TUISessionView extends TUIListView {
favIconPromise.then(base64Image => {
favicon.src = base64Image;
});
} else if (prop === "discarded") {
if (value) e.setAttribute("data-discarded", "");
else e.removeAttribute("data-discarded");
} else if (prop === "mutedInfo") {
let speaker = e.querySelector(".speaker");
if (speaker) {
Expand Down Expand Up @@ -1160,7 +1170,7 @@ class SearchDiv extends TUIEditableDiv {
if (value.trim() === "") {
this.tabsList.filter(value, undefined);
} else {
let ids = this.sess.getAllTabIdsAsStrings();
let ids = this.tabsList.children(this.tabsList.root, true, ele => !this.tabsList.isElementHiddenBy(ele, "manual-filter") && ele.hasAttribute("data-tab-id")).map(ele => ele.getAttribute("data-tab-id"));
await browser.runtime.sendMessage({
_: "search",
queryString: value,
Expand Down Expand Up @@ -1416,6 +1426,8 @@ class TUITabsList extends TUIListDataInterpret {
tmp.appendChild(tmp2);
entry.appendChild(tmp);

if (data.discarded) entry.setAttribute("data-discarded", "");

tmp = document.createElement("div");
tmp.className = "single-controls";
let pin = document.createElement("span");
Expand Down Expand Up @@ -1447,7 +1459,7 @@ class TUITabsList extends TUIListDataInterpret {
actions = new TTabActions(tabId);
}
if (evt["type"] && evt["type"] === "contextmenu") {
let moveTabs;
let moveTabs, unload, reload;
openContextMenu(evt, new TUIMenu(
moveTabs = new TUISubMenu(() => {}, (selection) => {
if (selection.data.windowId !== undefined) {
Expand All @@ -1463,8 +1475,16 @@ class TUITabsList extends TUIListDataInterpret {
return new TUIMenuItem(WindowName.getInstance(windowId).displayedValue, "", undefined, undefined, {
windowId
});
}), new TUIMenuHR(), new TUIMenuItem("A New Window", "", undefined, undefined, { })], false, "Move Tab(s) to...", "")
).callback(() => {}, (_) => { }).make());
}), new TUIMenuHR(), new TUIMenuItem("A New Window", "", undefined, undefined, { })], false, "Move Tab(s) to...", ""),
unload = new TUIMenuItem("Unload", ""),
reload = new TUIMenuItem("Reload", "../icons/refresh.svg")
).callback(() => {}, (state) => {
if (state.target === unload) {
actions.discard();
} else if (state.target === reload) {
actions.reload();
}
}).make());
} else if (evt.target.classList.contains("pin") || evt.key === "p" || evt.key === "P") {
await actions.pin(!tabObj.pinned);
} else if (evt.target.classList.contains("speaker") || evt.key === "m" || evt.key === "M") {
Expand Down Expand Up @@ -1856,6 +1876,51 @@ class TUITabsList extends TUIListDataInterpret {
}
}
});
} else if (evt.key === 'd' || evt.key === 'D') {
// Clear manual filters
for (let child of tabsList.children(tabsList.root, true)) {
tabsList.showElement(child, "manual-filter");
}
// Hide the manual filter indicator
let indicator = tabsList.root.parentElement.querySelector(".manual-filter-indicator");
if (indicator) indicator.setAttribute("data-inactive", "");

// Clear the search
search.value = "";
search.onInput(search.value);
search.root.blur();
} else if (evt.key === 'f' || evt.key === 'F') {
let allMultiselected = tabsList.getSelected();
for (let ele of (allMultiselected.length === 0 ?
tabsList.children(tabsList.root, true, TUIList.isElementHidden) :
tabsList.children(tabsList.root, true, ele => ele.hasAttribute("data-tab-id") && !allMultiselected.includes(ele)))) {
tabsList.hideElement(ele, "manual-filter");
}

let indicator = tabsList.root.parentElement.querySelector(".manual-filter-indicator");
if (!indicator) {
indicator = document.createElement("div");
indicator.classList.add("manual-filter-indicator");
let rect = tabsList.root.getBoundingClientRect();
let rectParent = tabsList.root.parentElement.getBoundingClientRect();
indicator.style.width = `${rect.width}px`;
indicator.style.height = `${Math.min(rect.height, rectParent.height)}px`;
indicator.style.left = `${rect.left}px`;
indicator.style.top = `${Math.max(rect.top, rectParent.top)}px`;
let resizeObserver = new ResizeObserver((_) => {
let rect = tabsList.root.getBoundingClientRect();
let rectParent = tabsList.root.parentElement.getBoundingClientRect();
indicator.style.width = `${rect.width}px`;
indicator.style.height = `${Math.min(rect.height, rectParent.height)}px`;
indicator.style.left = `${rect.left}px`;
indicator.style.top = `${Math.max(rect.top, rectParent.top)}px`;
});
resizeObserver.observe(tabsList.root);
resizeObserver.observe(tabsList.root.parentElement);
tabsList.root.parentElement.appendChild(indicator);
}
indicator.removeAttribute("data-inactive");
t_resetAnimation(indicator);
} else if (evt.key === 'i' || evt.key === 'I') {
tabsList.modifySelected((processSelectCB) => {
for (let child of tabsList.children(tabsList.root, true)) {
Expand Down
12 changes: 12 additions & 0 deletions src/tapi/taction.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,18 @@ export class TTabActions {
openerTabId(v) {
return Promise.all(this.ids.map(id => browser.tabs.update(id, {openerTabId: v})));
}
reload(bypassCache=false) {
return Promise.all(this.ids.map(id => browser.tabs.reload(id, {bypassCache})));
}
discard() {
if (TargetBrowser === "firefox") {
return browser.tabs.discard(this.ids);
} else if (browser.tabs.discard) {
return Promise.all(this.ids.map(id => browser.tabs.discard(id)));
} else {
// This should be fine for now, since there's not much that can be done if the API itself is unavailable.
}
}
remove() {
return browser.tabs.remove(this.ids);
}
Expand Down

0 comments on commit 23cba20

Please sign in to comment.