Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Page zoom support #49

Merged
merged 4 commits into from
Jan 5, 2024
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
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.

Loading