Skip to content

Commit

Permalink
Merge pull request #1891 from googlefonts/fontoverview-settings
Browse files Browse the repository at this point in the history
[font overview] use settings object
  • Loading branch information
justvanrossum authored Dec 23, 2024
2 parents eae6860 + d66683c commit 9333042
Show file tree
Hide file tree
Showing 7 changed files with 151 additions and 122 deletions.
63 changes: 63 additions & 0 deletions src/fontra/client/core/glyph-organizer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
export class GlyphOrganizer {
constructor() {
this._glyphNamesListFilterFunc = (item) => true; // pass all through
}

setSearchString(searchString) {
const searchItems = searchString.split(/\s+/).filter((item) => item.length);
const hexSearchItems = searchItems
.filter((item) => [...item].length === 1) // num chars, not utf16 units!
.map((item) => item.codePointAt(0).toString(16).toUpperCase().padStart(4, "0"));
searchItems.push(...hexSearchItems);
this._glyphNamesListFilterFunc = (item) => glyphFilterFunc(item, searchItems);
}

sortGlyphs(glyphs) {
glyphs = [...glyphs];
glyphs.sort(glyphItemSortFunc);
return glyphs;
}

filterGlyphs(glyphs) {
return glyphs.filter(this._glyphNamesListFilterFunc);
}
}

function glyphFilterFunc(item, searchItems) {
if (!searchItems.length) {
return true;
}
for (const searchString of searchItems) {
if (item.glyphName.indexOf(searchString) >= 0) {
return true;
}
if (item.codePoints[0] !== undefined) {
const char = String.fromCodePoint(item.codePoints[0]);
if (searchString === char) {
return true;
}
}
}
return false;
}

function glyphItemSortFunc(item1, item2) {
const uniCmp = compare(item1.codePoints[0], item2.codePoints[0]);
const glyphNameCmp = compare(item1.glyphName, item2.glyphName);
return uniCmp ? uniCmp : glyphNameCmp;
}

function compare(a, b) {
// sort undefined at the end
if (a === b) {
return 0;
} else if (a === undefined) {
return 1;
} else if (b === undefined) {
return -1;
} else if (a < b) {
return -1;
} else {
return 1;
}
}
18 changes: 9 additions & 9 deletions src/fontra/client/web-components/glyph-cell-view.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import * as html from "/core/html-utils.js";
import { translate } from "/core/localization.js";
import { ObservableController } from "/core/observable-object.js";
import { difference, intersection, symmetricDifference, union } from "/core/set-ops.js";
import { enumerate } from "/core/utils.js";
import { GlyphCell } from "/web-components/glyph-cell.js";
import { Accordion } from "/web-components/ui-accordion.js";

export class GlyphCellView extends HTMLElement {
constructor(fontController, locationController) {
constructor(fontController, settingsController, options) {
super();

this.fontController = fontController;
this.locationController = locationController;
this.settingsController = settingsController;
this.locationKey = options?.locationKey || "fontLocationSourceMapped";
this.glyphSelectionKey = options?.glyphSelectionKey || "glyphSelection";

this.glyphSelectionController = new ObservableController({ selection: new Set() });
this.glyphSelectionController.addKeyListener("selection", (event) => {
this.settingsController.addKeyListener(this.glyphSelectionKey, (event) => {
const selection = event.newValue;
const diff = symmetricDifference(selection, event.oldValue);
this.forEachGlyphCell((glyphCell) => {
Expand Down Expand Up @@ -130,8 +130,8 @@ export class GlyphCellView extends HTMLElement {
this.fontController,
glyphName,
codePoints,
this.locationController,
"fontLocationSourceMapped"
this.settingsController,
this.locationKey
);
glyphCell.onclick = (event) => {
this.handleSingleClick(event, glyphCell);
Expand All @@ -158,11 +158,11 @@ export class GlyphCellView extends HTMLElement {
}

get glyphSelection() {
return this.glyphSelectionController.model.selection;
return this.settingsController.model[this.glyphSelectionKey];
}

set glyphSelection(selection) {
this.glyphSelectionController.model.selection = selection;
this.settingsController.model[this.glyphSelectionKey] = selection;
}

forEachGlyphCell(func) {
Expand Down
2 changes: 0 additions & 2 deletions src/fontra/client/web-components/glyph-cell.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,6 @@ export class GlyphCell extends UnlitElement {
.glyph-name-label {
font-size: 0.85em;
padding-left: 0.3em;
padding-right: 0.3em;
overflow-x: hidden;
text-overflow: ellipsis;
text-overflow: ellipsis;
Expand Down
76 changes: 18 additions & 58 deletions src/fontra/client/web-components/glyph-search-field.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export class GlyphSearchField extends SimpleElement {
}
`;

constructor() {
constructor(options) {
super();
this.searchField = html.input({
type: "text",
Expand All @@ -37,76 +37,36 @@ export class GlyphSearchField extends SimpleElement {
oninput: (event) => this._searchFieldChanged(event),
});

this._glyphNamesListFilterFunc = (item) => true; // pass all through
if (options?.settingsController) {
this._settingsController = options.settingsController;
this._searchStringKey = options.searchStringKey || "searchString";
this._settingsController.addKeyListener(this._searchStringKey, (event) => {
this.searchField.value = event.newValue;
});
}

this.shadowRoot.appendChild(this.searchField);
}

get searchString() {
return this.searchField.value;
}

focusSearchField() {
this.searchField.focus();
}

_searchFieldChanged(event) {
event.preventDefault();
event.stopImmediatePropagation();
const value = event.target.value;
const searchItems = value.split(/\s+/).filter((item) => item.length);
const hexSearchItems = searchItems
.filter((item) => [...item].length === 1) // num chars, not utf16 units!
.map((item) => item.codePointAt(0).toString(16).toUpperCase().padStart(4, "0"));
searchItems.push(...hexSearchItems);
this._glyphNamesListFilterFunc = (item) => glyphFilterFunc(item, searchItems);

this.onSearchFieldChanged?.(event);
}

sortGlyphs(glyphs) {
// This arguably doesn't belong here
glyphs = [...glyphs];
glyphs.sort(glyphItemSortFunc);
return glyphs;
}
if (this._settingsController) {
this._settingsController.model[this._searchStringKey] = value;
}

filterGlyphs(glyphs) {
return glyphs.filter(this._glyphNamesListFilterFunc);
this.onSearchFieldChanged?.(event);
}
}

customElements.define("glyph-search-field", GlyphSearchField);

function glyphFilterFunc(item, searchItems) {
if (!searchItems.length) {
return true;
}
for (const searchString of searchItems) {
if (item.glyphName.indexOf(searchString) >= 0) {
return true;
}
if (item.codePoints[0] !== undefined) {
const char = String.fromCodePoint(item.codePoints[0]);
if (searchString === char) {
return true;
}
}
}
return false;
}

function glyphItemSortFunc(item1, item2) {
const uniCmp = compare(item1.codePoints[0], item2.codePoints[0]);
const glyphNameCmp = compare(item1.glyphName, item2.glyphName);
return uniCmp ? uniCmp : glyphNameCmp;
}

function compare(a, b) {
// sort undefined at the end
if (a === b) {
return 0;
} else if (a === undefined) {
return 1;
} else if (b === undefined) {
return -1;
} else if (a < b) {
return -1;
} else {
return 1;
}
}
10 changes: 7 additions & 3 deletions src/fontra/client/web-components/glyph-search-list.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { GlyphSearchField } from "./glyph-search-field.js";
import { UIList } from "./ui-list.js";
import { GlyphOrganizer } from "/core/glyph-organizer.js";
import * as html from "/core/html-utils.js";
import { SimpleElement } from "/core/html-utils.js";
import {
Expand All @@ -25,12 +26,14 @@ export class GlyphSearchList extends SimpleElement {
constructor() {
super();

this.glyphOrganizer = new GlyphOrganizer();

this.searchField = new GlyphSearchField();
this.glyphNamesList = this._makeGlyphNamesList();

this.throttledUpdate = throttleCalls(() => this.update(), 50);

this.searchField.oninput = (event) => this.throttledUpdate();
this.searchField.onSearchFieldChanged = (event) => this.throttledUpdate();

this.shadowRoot.appendChild(this.searchField);
this.shadowRoot.appendChild(this.glyphNamesList);
Expand Down Expand Up @@ -101,14 +104,15 @@ export class GlyphSearchList extends SimpleElement {
}

updateGlyphNamesListContent() {
this.glyphsListItems = this.searchField.sortGlyphs(
this.glyphOrganizer.setSearchString(this.searchField.searchString);
this.glyphsListItems = this.glyphOrganizer.sortGlyphs(
glyphMapToItemList(this.glyphMap)
);
this._setFilteredGlyphNamesListContent();
}

_setFilteredGlyphNamesListContent() {
const filteredGlyphItems = this.searchField.filterGlyphs(this.glyphsListItems);
const filteredGlyphItems = this.glyphOrganizer.filterGlyphs(this.glyphsListItems);
this.glyphNamesList.setItems(filteredGlyphItems);
}

Expand Down
53 changes: 39 additions & 14 deletions src/fontra/views/fontoverview/fontoverview.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { FontOverviewNavigation } from "./panel-navigation.js";
import { GlyphOrganizer } from "/core/glyph-organizer.js";
import * as html from "/core/html-utils.js";
import { loaderSpinner } from "/core/loader-spinner.js";
import { translate } from "/core/localization.js";
Expand All @@ -20,15 +21,7 @@ export class FontOverviewController extends ViewController {
constructor(font) {
super(font);

this.locationController = new ObservableController({
fontLocationSourceMapped: {},
});

this.updateGlyphSelection = throttleCalls(() => this._updateGlyphSelection(), 50);

// document.addEventListener("keydown", (event) => this.handleKeyDown(event));
// document.addEventListener("keyup", (event) => this.handleKeyUp(event));
// this.previousArrowDirection = "ArrowRight";
}

async start() {
Expand All @@ -38,6 +31,40 @@ export class FontOverviewController extends ViewController {
async _start() {
await this.fontController.initialize();

this.fontSources = await this.fontController.getSources();

this.fontOverviewSettingsController = new ObservableController({
searchString: "",
fontSourceIdentifier: null,
fontLocationSourceMapped: {},
glyphSelection: new Set(),
});
this.fontOverviewSettings = this.fontOverviewSettingsController.model;

this.fontOverviewSettingsController.addKeyListener(
"fontSourceIdentifier",
(event) => {
const sourceLocation = {
...this.fontSources[event.newValue]?.location,
}; // A font may not have any font sources, therefore the ?-check

this.fontOverviewSettings.fontLocationSourceMapped =
this.fontController.mapSourceLocationToUserLocation(sourceLocation);
}
);
// Note: once we add an axis slider UI, we should do the opposite mapping,
// too, from location to source identifier

this.fontOverviewSettings.fontSourceIdentifier =
this.fontController.fontSourcesInstancer.defaultSourceIdentifier;

this.fontOverviewSettingsController.addKeyListener("searchString", (event) => {
this.glyphOrganizer.setSearchString(event.newValue);
this.updateGlyphSelection();
});

this.glyphOrganizer = new GlyphOrganizer();

const rootSubscriptionPattern = {};
for (const rootKey of this.fontController.getRootKeys()) {
rootSubscriptionPattern[rootKey] = null;
Expand All @@ -49,12 +76,10 @@ export class FontOverviewController extends ViewController {
const glyphCellViewContainer = document.querySelector("#glyph-cell-view-container");

this.navigation = new FontOverviewNavigation(this);
await this.navigation.start();
this.navigation.onSearchFieldChanged = this.updateGlyphSelection;

this.glyphCellView = new GlyphCellView(
this.fontController,
this.locationController
this.fontOverviewSettingsController
);

// // This is how we can change the cell size:
Expand All @@ -72,7 +97,7 @@ export class FontOverviewController extends ViewController {
}

_updateGlyphItemList() {
this._glyphItemList = this.navigation.sortGlyphs(
this._glyphItemList = this.glyphOrganizer.sortGlyphs(
glyphMapToItemList(this.fontController.glyphMap)
);
this._updateGlyphSelection();
Expand All @@ -82,7 +107,7 @@ export class FontOverviewController extends ViewController {
// We possibly need to be smarter about this:
this.glyphCellView.parentElement.scrollTop = 0;

const glyphItemList = this.navigation.filterGlyphs(this._glyphItemList);
const glyphItemList = this.glyphOrganizer.filterGlyphs(this._glyphItemList);
this.glyphCellView.setGlyphItems(glyphItemList);
}

Expand All @@ -93,7 +118,7 @@ export class FontOverviewController extends ViewController {
openSelectedGlyphs() {
openGlyphsInEditor(
this.glyphCellView.getSelectedGlyphInfo(),
this.navigation.getUserLocation(),
this.fontOverviewSettings.fontLocationSourceMapped,
this.fontController.glyphMap
);
}
Expand Down
Loading

0 comments on commit 9333042

Please sign in to comment.