Skip to content

Commit

Permalink
🚧 WIP: Start work on customizable UI
Browse files Browse the repository at this point in the history
  • Loading branch information
kierandrewett committed Sep 22, 2023
1 parent 6a0bf92 commit d034770
Show file tree
Hide file tree
Showing 31 changed files with 1,612 additions and 1,213 deletions.
15 changes: 7 additions & 8 deletions base/content/browser-commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -200,14 +200,13 @@ var gDotCommands = {
name: "browser.toolbar.toggle",

action: ({ gDot, name }) => {
const toolbar = gDot.getToolbarByName(name);
if (!toolbar) {
throw new Error(
`Toolbar with name '${name}' could not be found!`
);
}

toolbar.toggleCollapsed();
// const toolbar = gDot.getToolbarByName(name);
// if (!toolbar) {
// throw new Error(
// `Toolbar with name '${name}' could not be found!`
// );
// }
// toolbar.toggleCollapsed();
},

enabled: () => true,
Expand Down
1 change: 1 addition & 0 deletions base/content/browser-elements.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ let elements = {
};

let noCallbackElements = [
"chrome://dot/content/customizableui/components/customizable-element.js",
"chrome://dot/content/widgets/browser-addressbar-panel.js",
"chrome://dot/content/widgets/browser-customizable-area.js",
"chrome://dot/content/widgets/browser-contextual-element.js",
Expand Down
233 changes: 6 additions & 227 deletions base/content/browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ ChromeUtils.defineESModuleGetters(globalThis, {
DotAppConstants: "resource://gre/modules/DotAppConstants.sys.mjs"
});

var { DotCustomizableUI } = ChromeUtils.importESModule(
"resource:///modules/DotCustomizableUI.sys.mjs"
var { BrowserCustomizable } = ChromeUtils.importESModule(
"resource:///modules/BrowserCustomizable.sys.mjs"
);

var { NavigationHelper } = ChromeUtils.importESModule(
Expand All @@ -33,17 +33,13 @@ var { NativeTitlebar } = ChromeUtils.importESModule(
class BrowserApplication extends MozHTMLElement {
constructor() {
super();

this.mutationObserver = new MutationObserver(
this.observeMutations.bind(this)
);
this.intersectionObserver = new IntersectionObserver(
this.observeToolbarIntersections.bind(this)
);
}

_done = false;

/** @type {typeof BrowserCustomizable.prototype} */
customizable = null;

/** @type {typeof BrowserTabs.prototype} */
tabs = null;

Expand Down Expand Up @@ -88,233 +84,17 @@ class BrowserApplication extends MozHTMLElement {
return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
}

/**
* Obtains an area element for an area ID
* @param {string} area
* @returns {HTMLElement}
*/
getAreaElement(area) {
if (area == "menubar" || area == "toolbar" || area == "navbar") {
return /** @type {BrowserToolbar} */ (
html("browser-toolbar", { slot: area })
);
}

return this.querySelector(`[slot=${area}]`);
}

/**
* @type {Set<BrowserToolbar>}
*/
_toolbars = new Set();

/**
* An index sorted list of BrowserToolbars
* @type {BrowserToolbar[]}
*/
get toolbars() {
return Array.from(this._toolbars).sort(
(a, b) => a.getBoundingClientRect().y - b.getBoundingClientRect().y
);
}

/**
* Locates a toolbar by its name
* @param {string} name
* @returns {BrowserToolbar | null}
*/
getToolbarByName(name) {
return this.toolbars.find((tb) => tb.name === name);
}

_forcingNativeCSD = false;

/** @type {MutationCallback} */
observeMutations(mutations) {
for (const mut of mutations) {
for (const node of mut.addedNodes) {
if (
node instanceof BrowserToolbar &&
!this._toolbars.has(node)
) {
this.intersectionObserver.observe(node);
this._toolbars.add(node);
}
}

for (const node of mut.removedNodes) {
if (
node instanceof BrowserToolbar &&
this._toolbars.has(node)
) {
this.intersectionObserver.unobserve(node);
this._toolbars.delete(node);
}
}
}
}

/** @type {IntersectionObserverCallback} */
observeToolbarIntersections(intersections) {
for (const intersection of intersections) {
if (intersection.target instanceof BrowserToolbar) {
if (
intersection.intersectionRatio === 0 ||
intersection.intersectionRatio === 1
) {
this.computeInitialToolbar();
}
}
}
}

/**
* Recomputes the "initial" toolbar
*/
computeInitialToolbar() {
if (this._forcingNativeCSD && !window.fullScreen) {
this._forcingNativeCSD = false;
NativeTitlebar.set(false, false);
}

let oldToolbars = [];

for (const toolbar of this.toolbars) {
if (toolbar.hasAttribute("initial")) {
oldToolbars.push(toolbar);
}
}

let foundNewInitial = false;

for (const toolbar of this.toolbars) {
const bounds = toolbar.getBoundingClientRect();

toolbar.setAttribute(
"orientation",
bounds.width > bounds.height ? "horizontal" : "vertical"
);

// Skip toolbars that are hidden
if (bounds.width === 0 || bounds.height === 0) {
continue;
}

// Once we have found a suitable toolbar to
// make initial, return early, we're done here
toolbar.toggleAttribute("initial", true);
oldToolbars.forEach((t) => t.removeAttribute("initial"));
gDot.style.setProperty(
"--browser-csd-height",
toolbar.getBoundingClientRect().height + "px"
);
gDot.style.setProperty(
"--browser-csd-width",
gDot.shadowRoot
.querySelector("browser-window-controls")
.getBoundingClientRect().width + "px"
);

foundNewInitial = true;
return;
}

// If we weren't able to find a single toolbar to make initial
// We will need to show the window controls over the top of everything
//
// If we're in fullscreen mode, we can skip this as we're expecting chrome
// to be hidden.
if (!foundNewInitial && !window.fullScreen) {
this._forcingNativeCSD = true;
NativeTitlebar.set(true, false);
}
}

/**
* Fetches a slot from the Shadow DOM by name
* @param {string} name
*/
getSlot(name) {
return this.shadowRoot.querySelector(`slot[name=${name}]`);
}

/**
*
* @param {string} name
* @param {number} [index] - The index to move the slot to, use undefined to move to end
*/
moveSlotTo(name, index) {
const slot = this.getSlot(name);

if (typeof index == "undefined") {
this.shadowRoot.append(slot);
this.computeInitialToolbar();
return;
}

if (index <= 0) {
this.shadowRoot.prepend(slot);
this.computeInitialToolbar();
return;
}

const slots = Array.from(this.shadowRoot.childNodes).filter(
(n) => /** @type {Element} */ (n).tagName == "slot" && n !== slot
);

if (index >= slots.length) {
this.shadowRoot.append(slot);
this.computeInitialToolbar();
return;
}

const beforeSlot = slots[index];
console.log("beforeSlot", beforeSlot);

this.shadowRoot.insertBefore(slot, beforeSlot);
this.computeInitialToolbar();
}

connectedCallback() {
if (this.delayConnectedCallback()) return;

this.attachShadow({ mode: "open" });

const areas = ["menubar", "toolbar", "navbar", "web-contents"];
for (const area of areas) {
if (!this.getSlot(area)) {
this.shadowRoot.appendChild(
html("slot", { name: area, part: area })
);
}

const areaElement = this.getAreaElement(area);

if (area == "web-contents") {
areaElement.hidden = false;
}

this.append(areaElement);
}

for (const node of this.childNodes) {
if (node instanceof BrowserToolbar && !this._toolbars.has(node)) {
this.intersectionObserver.observe(node);
this._toolbars.add(node);
}
}

this.shadowRoot.appendChild(
html("link", {
rel: "stylesheet",
href: "chrome://dot/skin/browser.css"
})
);

this.mutationObserver.observe(this, {
childList: true,
subtree: true
});
}

/**
Expand All @@ -325,12 +105,11 @@ class BrowserApplication extends MozHTMLElement {
throw new Error("Browser cannot be initialized twice!");
}

gDot.customizable = new BrowserCustomizable(this);
gDot.tabs = new BrowserTabs(window);
gDot.search = new BrowserSearch(window);
gDot.shortcuts = new BrowserShortcuts(window);

DotCustomizableUI.init(window);

gDotRoutines.init();

// Listens for changes to the reduced motion preference
Expand Down
4 changes: 1 addition & 3 deletions base/content/browser.xhtml
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,7 @@
<html:browser-tabs-collator></html:browser-tabs-collator>

<html:browser-application>
<div id="tabspanel" slot="web-contents" hidden="">

</div>
<div id="tabspanel" slot="web-contents"></div>
</html:browser-application>

<html:div id="a11y-announcement" role="alert"/>
Expand Down
28 changes: 20 additions & 8 deletions build/scripts/gen_js_module_types.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,19 +85,21 @@ async function generateMozModules() {
function declareModule(moduleURI, originURI, exportName) {
const moduleType = toPascalCase(exportName);

const match = dotModules[moduleType];

// Check if we have a dot module type for this module
if (dotModules[moduleType]) {
if (match) {
customImports.add(moduleType);
} else {
imports.add(toPascalCase(exportName));
}

return `declare module ${JSON.stringify(moduleURI)} {${
dotModules[moduleType] ? `\n import * as M from "./${dotModules[moduleType]}";` : ``
match ? `\n import * as M from "./${match}";` : ``
}
export const ${toPascalCase(exportName)}: ${
dotModules[moduleType] ? `typeof M.` : ``
}${toPascalCase(exportName)};
match ? `typeof M.` : ``
}${toPascalCase(exportName)};
}\n\n`;
}

Expand All @@ -115,12 +117,17 @@ async function generateMozModules() {
if (isSymlink) {
origin = readlinkSync(module);
} else {
const match = findOriginByName(module, [parse(module).ext.substring(1)]);
const match = findOriginByName(module, [
parse(module).ext.substring(1)
]);

if (match) {
origin = match;
} else {
console.warn("WARN: Could not find origin for", basename(module));
console.warn(
"WARN: Could not find origin for",
basename(module)
);
}
}

Expand All @@ -134,7 +141,9 @@ async function generateMozModules() {
relativeURI = ".." + relativeURI.split(rootDir)[1];
}

const strippedModuleURI = module.split(resolve(binDir, "modules"))[1].substring(1);
const strippedModuleURI = module
.split(resolve(binDir, "modules"))[1]
.substring(1);

[
`resource:///modules/${strippedModuleURI}`,
Expand Down Expand Up @@ -195,7 +204,10 @@ export interface AllMozResourceBindings {
${Array.from(resourceBindings.keys())
.sort(alphabeticalSort)
.map(
(i) => `${JSON.stringify(i)}: ${JSON.stringify(toPascalCase(resourceBindings.get(i)))}`
(i) =>
`${JSON.stringify(i)}: ${JSON.stringify(
toPascalCase(resourceBindings.get(i))
)}`
)
.join(";\n ")}
Expand Down
2 changes: 0 additions & 2 deletions components/csd/content/browser-window-controls.css
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ browser-window-controls[hidden] {
}

:host(browser-application)::part(csd) {
position: fixed;
right: 0;
height: var(--browser-csd-height);
max-height: var(--browser-csd-height);
transition: 0.3s height cubic-bezier(0.19, 1, 0.22, 1), 0.3s max-height cubic-bezier(0.19, 1, 0.22, 1);
Expand Down
Loading

0 comments on commit d034770

Please sign in to comment.