Skip to content

Commit

Permalink
✨ Page zoom support (#49)
Browse files Browse the repository at this point in the history
  • Loading branch information
trickypr authored Jan 5, 2024
1 parent 4097921 commit 31d0ce2
Show file tree
Hide file tree
Showing 13 changed files with 229 additions and 1 deletion.
16 changes: 16 additions & 0 deletions apps/content/src/browser/components/keybindings/Keybindings.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,22 @@
pref="browser.keybinds.previousTab"
on:command={() => setCurrentTabIndex(getCurrentTabIndex() - 1)}
/>

<Keybinding
pref="browser.keybinds.zoomIn"
on:command={() =>
runOnCurrentTab((tab) => tab.zoom.update((zoom) => zoom + 0.1))}
/>
<Keybinding
pref="browser.keybinds.zoomOut"
on:command={() =>
runOnCurrentTab((tab) => tab.zoom.update((zoom) => zoom - 0.1))}
/>
<Keybinding
pref="browser.keybinds.zoomReset"
on:command={() => runOnCurrentTab((tab) => tab.zoom.set(1))}
/>

{#each [1, 2, 3, 4, 5, 6, 7, 8] as tabNum}
<Keybinding
pref={`browser.keybinds.tab${tabNum}`}
Expand Down
4 changes: 4 additions & 0 deletions apps/content/src/browser/components/omnibox/Omnibox.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import Bookmarks from './Bookmarks.svelte'
import { pageActions } from '@browser/lib/modules/EPageActionsBindings'
import PageAction from './PageAction.svelte'
import ZoomDisplay from './ZoomDisplay.svelte'
const suggestionsModule = import('@shared/search/suggestions')
Expand All @@ -22,6 +23,7 @@
let selectedSuggestion = 0
$: uri = tab.uri
$: zoom = tab.zoom
async function generateSuggestions() {
const { suggestions: suggestionsMethod } = await suggestionsModule
Expand Down Expand Up @@ -89,6 +91,8 @@
}}
/>

<ZoomDisplay zoom={$zoom} on:click={() => zoom.set(1)} />

{#each $pageActions as [_extId, pageAction]}
{#if pageAction.shouldShow($uri.asciiSpec, tab.getTabId())}
<PageAction {pageAction} />
Expand Down
28 changes: 28 additions & 0 deletions apps/content/src/browser/components/omnibox/ZoomDisplay.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<script lang="ts">
export let zoom: number
</script>

{#if zoom != 1}
<button on:click>
{Math.round(zoom * 100)}%
</button>
{/if}

<style>
button {
background: var(--surface);
border: none;
cursor: pointer;
border-radius: 0.25rem;
height: 2rem;
margin-right: 0.125rem;
}
button:hover {
background: var(--surface-1);
}
</style>
1 change: 1 addition & 0 deletions apps/content/src/browser/lib/resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ export const resource = lazyESModuleGetters({
NetUtil: 'resource://gre/modules/NetUtil.sys.mjs',
PageThumbs: 'resource://gre/modules/PageThumbs.sys.mjs',
WindowTracker: 'resource://app/modules/BrowserWindowTracker.sys.mjs',
ZoomStore: 'resource://app/modules/ZoomStore.sys.mjs',
})
28 changes: 28 additions & 0 deletions apps/content/src/browser/lib/window/tab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { type ViewableWritable, viewableWritable } from '@experiment/shared'
import mitt from 'mitt'
import { type Writable, get, writable } from 'svelte/store'

import type { ZoomStoreEvents } from 'resource://app/modules/ZoomStore.sys.mjs'

import { type BookmarkTreeNode, search } from '@shared/ExtBookmarkAPI'

import { resource } from '../resources'
Expand Down Expand Up @@ -43,6 +45,8 @@ export class Tab {
public loading = writable(false)
public loadingProgress = writable(0)

public zoom = writable(1)

/**
* This is used by the omnibox to determine if text input should be focused.
*/
Expand All @@ -59,13 +63,27 @@ export class Tab {
this.goToUri(uri)
this.title.set(uri.asciiHost)

this.zoom.subscribe((newZoom) => {
if (
!this.browserElement.browsingContext ||
this.browserElement.fullZoom === newZoom
) {
return
}

this.browserElement.fullZoom = newZoom
resource.ZoomStore.setZoomForUri(this.uri.readOnce(), newZoom)
})
this.uri.subscribe(async (uri) =>
this.bookmarkInfo.set(
await search({ url: uri.spec }).then((r) =>
r.length > 0 ? (r[0] as BookmarkTreeNode) : null,
),
),
)

// Remember to unsubscribe from any listeners you register here!
resource.ZoomStore.events.on('setZoom', this.zoomChange)
}

public getId(): number {
Expand Down Expand Up @@ -147,6 +165,11 @@ export class Tab {
this.useProgressListener()
}

zoomChange = (event: ZoomStoreEvents['setZoom']) => {
if (this.uri.readOnce().asciiHost != event.host) return
this.zoom.set(event.zoom)
}

protected useProgressListener() {
this.progressListener.events.on('locationChange', (event) => {
if (!event.aWebProgress.isTopLevel) return
Expand All @@ -156,6 +179,8 @@ export class Tab {
this.uri.set(event.aLocation)
this.canGoBack.set(this.browserElement.canGoBack)
this.canGoForward.set(this.browserElement.canGoForward)

this.zoom.set(resource.ZoomStore.getZoomForUri(event.aLocation))
})

this.progressListener.events.on('progressPercent', this.loadingProgress.set)
Expand All @@ -178,6 +203,9 @@ export class Tab {
}

public destroy() {
this.removeEventListeners()
resource.ZoomStore.events.off('setZoom', this.zoomChange)

this.browserElement.remove()
}

Expand Down
4 changes: 4 additions & 0 deletions apps/content/src/settings/Settings.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@
<StringPref pref="browser.keybinds.nextTab">Next tab</StringPref>
<StringPref pref="browser.keybinds.previousTab">Previous Tab</StringPref>

<StringPref pref="browser.keybinds.zoomIn">Zoom in</StringPref>
<StringPref pref="browser.keybinds.zoomOut">Zoom out</StringPref>
<StringPref pref="browser.keybinds.zoomReset">Reset Zoom</StringPref>

{#each [1, 2, 3, 4, 5, 6, 7, 8] as tabNum}
<StringPref pref={`browser.keybinds.tab${tabNum}`}
>Jump to tab {tabNum}</StringPref
Expand Down
3 changes: 3 additions & 0 deletions apps/misc/static/defaults/pref/prefs.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ pref('browser.keybinds.newTab', 'accel+T');
pref('browser.keybinds.closeTab', 'accel+W');
pref('browser.keybinds.nextTab', 'accel+VK_TAB');
pref('browser.keybinds.previousTab', 'accel+shift+VK_TAB');
pref('browser.keybinds.zoomIn', 'accel+=');
pref('browser.keybinds.zoomOut', 'accel+-');
pref('browser.keybinds.zoomReset', 'accel+0');
pref('browser.keybinds.tab1', 'accel+1');
pref('browser.keybinds.tab2', 'accel+2');
pref('browser.keybinds.tab3', 'accel+3');
Expand Down
102 changes: 102 additions & 0 deletions apps/modules/lib/ZoomStore.sys.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// @ts-check
/// <reference types="@browser/link" />
import mitt from 'resource://app/modules/mitt.sys.mjs'

const ZOOM_STORE_FILE = PathUtils.join(PathUtils.profileDir, 'zoomstore.json')

/** @typedef {import("resource://app/modules/ZoomStore.sys.mjs").ZoomStoreInterface} ZoomStoreInterface */

/** @implements {ZoomStoreInterface} */
class ZoomStoreImpl {
/**
* @private
* @type {Map<string, number> | null}
*/
zoomPages = null

/** @type {import('resource://app/modules/mitt.sys.mjs').Emitter<import('resource://app/modules/ZoomStore.sys.mjs').ZoomStoreEvents>} */
events = mitt()

constructor() {
this.init()
}

/** @protected */
async init() {
if (this.zoomPages) {
return
}

if (!(await IOUtils.exists(ZOOM_STORE_FILE))) {
this.zoomPages = new Map()
return
}

try {
const pages = await IOUtils.readJSON(ZOOM_STORE_FILE)
if (this.zoomPages) {
return
}

this.zoomPages = new Map(pages)
} catch (e) {
console.error('Failed to load zoomStore from file: ', e)
return
}
}

/** @private */
async save() {
if (!this.zoomPages) {
return
}

const toWrite = Array.from(this.zoomPages.entries())
try {
await IOUtils.writeJSON(ZOOM_STORE_FILE, toWrite)
} catch (e) {
console.error('Failed to write zoomStore to file:', e)
return
}
}

/**
* @param {nsIURIType} uri The uri to check zoom for
* @returns {number}
*/
getZoomForUri(uri) {
return this.zoomPages?.get(uri.asciiHost) || 1
}

/**
* @param {nsIURIType} uri
* @param {number} zoom The zoom to store. If set to 1, will delete any stored values
*/
setZoomForUri(uri, zoom) {
try {
uri.host
} catch {
return
}

if (zoom === 1) {
this.zoomPages?.delete(uri.host)
this.events.emit('setZoom', { host: uri.host, zoom })
return
}

this.zoomPages?.set(uri.host, Math.round(zoom * 100) / 100)

// @ts-ignore
Services.tm.dispatchToMainThread(() => {
this.events.emit('setZoom', { host: uri.host, zoom })
this.save()
})
}
}

export const ZoomStore = new ZoomStoreImpl()
1 change: 1 addition & 0 deletions libs/link/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"license": "ISC",
"dependencies": {
"gecko-types": "github:quark-platform/gecko-types",
"gecko-types@latest": "link:quark-platform/gecko-types@latest",
"mitt": "^3.0.1"
}
}
1 change: 1 addition & 0 deletions libs/link/types/_link.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@
/// <reference path="./modules/mitt.d.ts" />
/// <reference path="./modules/PlacesUtils.d.ts" />
/// <reference path="./modules/typedImport.d.ts" />
/// <reference path="./modules/ZoomStore.d.ts" />
2 changes: 2 additions & 0 deletions libs/link/types/globals/Elements.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ declare interface XULBrowserElement extends HTMLElement {
canGoForward: boolean
goForward()
reload()
fullZoom: number
browsingContext?: unknown
loadURI(uri: nsIURIType, params?: LoadURIOptions)
browserId: number
mInitialized: boolean
Expand Down
28 changes: 28 additions & 0 deletions libs/link/types/modules/ZoomStore.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

declare module 'resource://app/modules/ZoomStore.sys.mjs' {
import { Emitter } from 'mitt'

export type ZoomStoreEvents = {
setZoom: { host: string; zoom: number }
}

export const ZoomStore: {
events: Emitter<ZoomStoreEvents>

getZoomForUri(uri: nsIURIType): number
setZoomForUri(uri: nsIURIType, zoom: number): void
}

export type ZoomStoreInterface = typeof ZoomStore
}

declare interface MozESMExportFile {
ZoomStore: 'resource://app/modules/ZoomStore.sys.mjs'
}

declare interface MozESMExportType {
ZoomStore: typeof import('resource://app/modules/ZoomStore.sys.mjs').ZoomStore
}
12 changes: 11 additions & 1 deletion pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 31d0ce2

Please sign in to comment.