зеркало из https://github.com/electron/electron.git
build: convert touch-bar to typescript (#24511)
This commit is contained in:
Родитель
3f37ff87d2
Коммит
4c3da359fc
|
@ -41,6 +41,10 @@ the button in the touch bar.
|
||||||
A `NativeImage` representing the button's current icon. Changing this value immediately updates the button
|
A `NativeImage` representing the button's current icon. Changing this value immediately updates the button
|
||||||
in the touch bar.
|
in the touch bar.
|
||||||
|
|
||||||
|
#### `touchBarButton.iconPosition`
|
||||||
|
|
||||||
|
A `String` - Can be `left`, `right` or `overlay`. Defaults to `overlay`.
|
||||||
|
|
||||||
#### `touchBarButton.enabled`
|
#### `touchBarButton.enabled`
|
||||||
|
|
||||||
A `Boolean` representing whether the button is in an enabled state.
|
A `Boolean` representing whether the button is in an enabled state.
|
||||||
|
|
|
@ -49,3 +49,7 @@ updates the control in the touch bar. Updating deep properties inside this array
|
||||||
|
|
||||||
An `Integer` representing the currently selected segment. Changing this value immediately updates the control
|
An `Integer` representing the currently selected segment. Changing this value immediately updates the control
|
||||||
in the touch bar. User interaction with the touch bar will update this value automatically.
|
in the touch bar. User interaction with the touch bar will update this value automatically.
|
||||||
|
|
||||||
|
#### `touchBarSegmentedControl.mode`
|
||||||
|
|
||||||
|
A `String` representing the current selection mode of the control. Can be `single`, `multiple` or `buttons`.
|
||||||
|
|
|
@ -11,3 +11,11 @@ Process: [Main](../tutorial/application-architecture.md#main-and-renderer-proces
|
||||||
* `small` - Small space between items. Maps to `NSTouchBarItemIdentifierFixedSpaceSmall`. This is the default.
|
* `small` - Small space between items. Maps to `NSTouchBarItemIdentifierFixedSpaceSmall`. This is the default.
|
||||||
* `large` - Large space between items. Maps to `NSTouchBarItemIdentifierFixedSpaceLarge`.
|
* `large` - Large space between items. Maps to `NSTouchBarItemIdentifierFixedSpaceLarge`.
|
||||||
* `flexible` - Take up all available space. Maps to `NSTouchBarItemIdentifierFlexibleSpace`.
|
* `flexible` - Take up all available space. Maps to `NSTouchBarItemIdentifierFlexibleSpace`.
|
||||||
|
|
||||||
|
### Instance Properties
|
||||||
|
|
||||||
|
The following properties are available on instances of `TouchBarSpacer`:
|
||||||
|
|
||||||
|
#### `touchBarSpacer.size`
|
||||||
|
|
||||||
|
A `String` representing the size of the spacer. Can be `small`, `large` or `flexible`.
|
||||||
|
|
|
@ -217,7 +217,7 @@ auto_filenames = {
|
||||||
"lib/browser/api/screen.ts",
|
"lib/browser/api/screen.ts",
|
||||||
"lib/browser/api/session.ts",
|
"lib/browser/api/session.ts",
|
||||||
"lib/browser/api/system-preferences.ts",
|
"lib/browser/api/system-preferences.ts",
|
||||||
"lib/browser/api/touch-bar.js",
|
"lib/browser/api/touch-bar.ts",
|
||||||
"lib/browser/api/tray.ts",
|
"lib/browser/api/tray.ts",
|
||||||
"lib/browser/api/view.ts",
|
"lib/browser/api/view.ts",
|
||||||
"lib/browser/api/views/image-view.ts",
|
"lib/browser/api/views/image-view.ts",
|
||||||
|
|
|
@ -1,365 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const { EventEmitter } = require('events');
|
|
||||||
|
|
||||||
let nextItemID = 1;
|
|
||||||
|
|
||||||
class TouchBar extends EventEmitter {
|
|
||||||
// Bind a touch bar to a window
|
|
||||||
static _setOnWindow (touchBar, window) {
|
|
||||||
if (window._touchBar != null) {
|
|
||||||
window._touchBar._removeFromWindow(window);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (touchBar == null) {
|
|
||||||
window._setTouchBarItems([]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Array.isArray(touchBar)) {
|
|
||||||
touchBar = new TouchBar(touchBar);
|
|
||||||
}
|
|
||||||
touchBar._addToWindow(window);
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor (options) {
|
|
||||||
super();
|
|
||||||
|
|
||||||
if (options == null) {
|
|
||||||
throw new Error('Must specify options object as first argument');
|
|
||||||
}
|
|
||||||
|
|
||||||
let { items, escapeItem } = options;
|
|
||||||
|
|
||||||
if (!Array.isArray(items)) {
|
|
||||||
items = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
this.changeListener = (item) => {
|
|
||||||
this.emit('change', item.id, item.type);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.windowListeners = {};
|
|
||||||
this.items = {};
|
|
||||||
this.ordereredItems = [];
|
|
||||||
this.escapeItem = escapeItem;
|
|
||||||
|
|
||||||
const registerItem = (item) => {
|
|
||||||
this.items[item.id] = item;
|
|
||||||
item.on('change', this.changeListener);
|
|
||||||
if (item.child instanceof TouchBar) {
|
|
||||||
item.child.ordereredItems.forEach(registerItem);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let hasOtherItemsProxy = false;
|
|
||||||
const idSet = new Set();
|
|
||||||
items.forEach((item) => {
|
|
||||||
if (!(item instanceof TouchBarItem)) {
|
|
||||||
throw new Error('Each item must be an instance of TouchBarItem');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item.type === 'other_items_proxy') {
|
|
||||||
if (!hasOtherItemsProxy) {
|
|
||||||
hasOtherItemsProxy = true;
|
|
||||||
} else {
|
|
||||||
throw new Error('Must only have one OtherItemsProxy per TouchBar');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!idSet.has(item.id)) {
|
|
||||||
idSet.add(item.id);
|
|
||||||
} else {
|
|
||||||
throw new Error('Cannot add a single instance of TouchBarItem multiple times in a TouchBar');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// register in separate loop after all items are validated
|
|
||||||
for (const item of items) {
|
|
||||||
this.ordereredItems.push(item);
|
|
||||||
registerItem(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
set escapeItem (item) {
|
|
||||||
if (item != null && !(item instanceof TouchBarItem)) {
|
|
||||||
throw new Error('Escape item must be an instance of TouchBarItem');
|
|
||||||
}
|
|
||||||
if (this.escapeItem != null) {
|
|
||||||
this.escapeItem.removeListener('change', this.changeListener);
|
|
||||||
}
|
|
||||||
this._escapeItem = item;
|
|
||||||
if (this.escapeItem != null) {
|
|
||||||
this.escapeItem.on('change', this.changeListener);
|
|
||||||
}
|
|
||||||
this.emit('escape-item-change', item);
|
|
||||||
}
|
|
||||||
|
|
||||||
get escapeItem () {
|
|
||||||
return this._escapeItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
_addToWindow (window) {
|
|
||||||
const { id } = window;
|
|
||||||
|
|
||||||
// Already added to window
|
|
||||||
if (Object.prototype.hasOwnProperty.call(this.windowListeners, id)) return;
|
|
||||||
|
|
||||||
window._touchBar = this;
|
|
||||||
|
|
||||||
const changeListener = (itemID) => {
|
|
||||||
window._refreshTouchBarItem(itemID);
|
|
||||||
};
|
|
||||||
this.on('change', changeListener);
|
|
||||||
|
|
||||||
const escapeItemListener = (item) => {
|
|
||||||
window._setEscapeTouchBarItem(item != null ? item : {});
|
|
||||||
};
|
|
||||||
this.on('escape-item-change', escapeItemListener);
|
|
||||||
|
|
||||||
const interactionListener = (event, itemID, details) => {
|
|
||||||
let item = this.items[itemID];
|
|
||||||
if (item == null && this.escapeItem != null && this.escapeItem.id === itemID) {
|
|
||||||
item = this.escapeItem;
|
|
||||||
}
|
|
||||||
if (item != null && item.onInteraction != null) {
|
|
||||||
item.onInteraction(details);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
window.on('-touch-bar-interaction', interactionListener);
|
|
||||||
|
|
||||||
const removeListeners = () => {
|
|
||||||
this.removeListener('change', changeListener);
|
|
||||||
this.removeListener('escape-item-change', escapeItemListener);
|
|
||||||
window.removeListener('-touch-bar-interaction', interactionListener);
|
|
||||||
window.removeListener('closed', removeListeners);
|
|
||||||
window._touchBar = null;
|
|
||||||
delete this.windowListeners[id];
|
|
||||||
const unregisterItems = (items) => {
|
|
||||||
for (const item of items) {
|
|
||||||
item.removeListener('change', this.changeListener);
|
|
||||||
if (item.child instanceof TouchBar) {
|
|
||||||
unregisterItems(item.child.ordereredItems);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
unregisterItems(this.ordereredItems);
|
|
||||||
if (this.escapeItem) {
|
|
||||||
this.escapeItem.removeListener('change', this.changeListener);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
window.once('closed', removeListeners);
|
|
||||||
this.windowListeners[id] = removeListeners;
|
|
||||||
|
|
||||||
window._setTouchBarItems(this.ordereredItems);
|
|
||||||
escapeItemListener(this.escapeItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
_removeFromWindow (window) {
|
|
||||||
const removeListeners = this.windowListeners[window.id];
|
|
||||||
if (removeListeners != null) removeListeners();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class TouchBarItem extends EventEmitter {
|
|
||||||
constructor () {
|
|
||||||
super();
|
|
||||||
this._addImmutableProperty('id', `${nextItemID++}`);
|
|
||||||
this._parents = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
_addImmutableProperty (name, value) {
|
|
||||||
Object.defineProperty(this, name, {
|
|
||||||
get: function () {
|
|
||||||
return value;
|
|
||||||
},
|
|
||||||
set: function () {
|
|
||||||
throw new Error(`Cannot override property ${name}`);
|
|
||||||
},
|
|
||||||
enumerable: true,
|
|
||||||
configurable: false
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_addLiveProperty (name, initialValue) {
|
|
||||||
const privateName = `_${name}`;
|
|
||||||
this[privateName] = initialValue;
|
|
||||||
Object.defineProperty(this, name, {
|
|
||||||
get: function () {
|
|
||||||
return this[privateName];
|
|
||||||
},
|
|
||||||
set: function (value) {
|
|
||||||
this[privateName] = value;
|
|
||||||
this.emit('change', this);
|
|
||||||
},
|
|
||||||
enumerable: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_addParent (item) {
|
|
||||||
const existing = this._parents.some(test => test.id === item.id);
|
|
||||||
if (!existing) {
|
|
||||||
this._parents.push({
|
|
||||||
id: item.id,
|
|
||||||
type: item.type
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TouchBar.TouchBarButton = class TouchBarButton extends TouchBarItem {
|
|
||||||
constructor (config) {
|
|
||||||
super();
|
|
||||||
if (config == null) config = {};
|
|
||||||
this._addImmutableProperty('type', 'button');
|
|
||||||
this._addLiveProperty('label', config.label);
|
|
||||||
this._addLiveProperty('accessibilityLabel', config.accessibilityLabel);
|
|
||||||
this._addLiveProperty('backgroundColor', config.backgroundColor);
|
|
||||||
this._addLiveProperty('icon', config.icon);
|
|
||||||
this._addLiveProperty('iconPosition', config.iconPosition);
|
|
||||||
this._addLiveProperty('enabled', typeof config.enabled !== 'boolean' ? true : config.enabled);
|
|
||||||
if (typeof config.click === 'function') {
|
|
||||||
this._addImmutableProperty('onInteraction', () => {
|
|
||||||
config.click();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
TouchBar.TouchBarColorPicker = class TouchBarColorPicker extends TouchBarItem {
|
|
||||||
constructor (config) {
|
|
||||||
super();
|
|
||||||
if (config == null) config = {};
|
|
||||||
this._addImmutableProperty('type', 'colorpicker');
|
|
||||||
this._addLiveProperty('availableColors', config.availableColors);
|
|
||||||
this._addLiveProperty('selectedColor', config.selectedColor);
|
|
||||||
|
|
||||||
if (typeof config.change === 'function') {
|
|
||||||
this._addImmutableProperty('onInteraction', (details) => {
|
|
||||||
this._selectedColor = details.color;
|
|
||||||
config.change(details.color);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
TouchBar.TouchBarGroup = class TouchBarGroup extends TouchBarItem {
|
|
||||||
constructor (config) {
|
|
||||||
super();
|
|
||||||
if (config == null) config = {};
|
|
||||||
this._addImmutableProperty('type', 'group');
|
|
||||||
const defaultChild = (config.items instanceof TouchBar) ? config.items : new TouchBar(config.items);
|
|
||||||
this._addLiveProperty('child', defaultChild);
|
|
||||||
this.child.ordereredItems.forEach((item) => item._addParent(this));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
TouchBar.TouchBarLabel = class TouchBarLabel extends TouchBarItem {
|
|
||||||
constructor (config) {
|
|
||||||
super();
|
|
||||||
if (config == null) config = {};
|
|
||||||
this._addImmutableProperty('type', 'label');
|
|
||||||
this._addLiveProperty('label', config.label);
|
|
||||||
this._addLiveProperty('accessibilityLabel', config.accessibilityLabel);
|
|
||||||
this._addLiveProperty('textColor', config.textColor);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
TouchBar.TouchBarPopover = class TouchBarPopover extends TouchBarItem {
|
|
||||||
constructor (config) {
|
|
||||||
super();
|
|
||||||
if (config == null) config = {};
|
|
||||||
this._addImmutableProperty('type', 'popover');
|
|
||||||
this._addLiveProperty('label', config.label);
|
|
||||||
this._addLiveProperty('icon', config.icon);
|
|
||||||
this._addLiveProperty('showCloseButton', config.showCloseButton);
|
|
||||||
const defaultChild = (config.items instanceof TouchBar) ? config.items : new TouchBar(config.items);
|
|
||||||
this._addLiveProperty('child', defaultChild);
|
|
||||||
this.child.ordereredItems.forEach((item) => item._addParent(this));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
TouchBar.TouchBarSlider = class TouchBarSlider extends TouchBarItem {
|
|
||||||
constructor (config) {
|
|
||||||
super();
|
|
||||||
if (config == null) config = {};
|
|
||||||
this._addImmutableProperty('type', 'slider');
|
|
||||||
this._addLiveProperty('label', config.label);
|
|
||||||
this._addLiveProperty('minValue', config.minValue);
|
|
||||||
this._addLiveProperty('maxValue', config.maxValue);
|
|
||||||
this._addLiveProperty('value', config.value);
|
|
||||||
|
|
||||||
if (typeof config.change === 'function') {
|
|
||||||
this._addImmutableProperty('onInteraction', (details) => {
|
|
||||||
this._value = details.value;
|
|
||||||
config.change(details.value);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
TouchBar.TouchBarSpacer = class TouchBarSpacer extends TouchBarItem {
|
|
||||||
constructor (config) {
|
|
||||||
super();
|
|
||||||
if (config == null) config = {};
|
|
||||||
this._addImmutableProperty('type', 'spacer');
|
|
||||||
this._addImmutableProperty('size', config.size);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
TouchBar.TouchBarSegmentedControl = class TouchBarSegmentedControl extends TouchBarItem {
|
|
||||||
constructor (config) {
|
|
||||||
super();
|
|
||||||
if (config == null) config = {};
|
|
||||||
this._addImmutableProperty('type', 'segmented_control');
|
|
||||||
this._addLiveProperty('segmentStyle', config.segmentStyle);
|
|
||||||
this._addLiveProperty('segments', config.segments || []);
|
|
||||||
this._addLiveProperty('selectedIndex', config.selectedIndex);
|
|
||||||
this._addLiveProperty('mode', config.mode);
|
|
||||||
|
|
||||||
if (typeof config.change === 'function') {
|
|
||||||
this._addImmutableProperty('onInteraction', (details) => {
|
|
||||||
this._selectedIndex = details.selectedIndex;
|
|
||||||
config.change(details.selectedIndex, details.isSelected);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
TouchBar.TouchBarScrubber = class TouchBarScrubber extends TouchBarItem {
|
|
||||||
constructor (config) {
|
|
||||||
super();
|
|
||||||
if (config == null) config = {};
|
|
||||||
let { select, highlight } = config;
|
|
||||||
this._addImmutableProperty('type', 'scrubber');
|
|
||||||
this._addLiveProperty('items', config.items);
|
|
||||||
this._addLiveProperty('selectedStyle', config.selectedStyle || null);
|
|
||||||
this._addLiveProperty('overlayStyle', config.overlayStyle || null);
|
|
||||||
this._addLiveProperty('showArrowButtons', config.showArrowButtons || false);
|
|
||||||
this._addLiveProperty('mode', config.mode || 'free');
|
|
||||||
|
|
||||||
const cont = typeof config.continuous === 'undefined' ? true : config.continuous;
|
|
||||||
this._addLiveProperty('continuous', cont);
|
|
||||||
|
|
||||||
if (typeof select === 'function' || typeof highlight === 'function') {
|
|
||||||
if (select == null) select = () => {};
|
|
||||||
if (highlight == null) highlight = () => {};
|
|
||||||
this._addImmutableProperty('onInteraction', (details) => {
|
|
||||||
if (details.type === 'select' && typeof select === 'function') {
|
|
||||||
select(details.selectedIndex);
|
|
||||||
} else if (details.type === 'highlight' && typeof highlight === 'function') {
|
|
||||||
highlight(details.highlightedIndex);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
TouchBar.TouchBarOtherItemsProxy = class TouchBarOtherItemsProxy extends TouchBarItem {
|
|
||||||
constructor (config) {
|
|
||||||
super();
|
|
||||||
this._addImmutableProperty('type', 'other_items_proxy');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = TouchBar;
|
|
|
@ -0,0 +1,459 @@
|
||||||
|
import { EventEmitter } from 'events';
|
||||||
|
|
||||||
|
let nextItemID = 1;
|
||||||
|
|
||||||
|
const hiddenProperties = Symbol('hidden touch bar props');
|
||||||
|
|
||||||
|
const extendConstructHook = (target: any, hook: Function) => {
|
||||||
|
const existingHook = target._hook;
|
||||||
|
target._hook = function () {
|
||||||
|
hook.call(this);
|
||||||
|
if (existingHook) existingHook.call(this);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const ImmutableProperty = <T extends TouchBarItem<any>>(def: (config: T extends TouchBarItem<infer C> ? C : never, setInternalProp: <K extends keyof T>(k: K, v: T[K]) => void) => any) => (target: T, propertyKey: keyof T) => {
|
||||||
|
extendConstructHook(target as any, function (this: T) {
|
||||||
|
(this as any)[hiddenProperties][propertyKey] = def((this as any)._config, (k, v) => {
|
||||||
|
(this as any)[hiddenProperties][k] = v;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
Object.defineProperty(target, propertyKey, {
|
||||||
|
get: function () {
|
||||||
|
return (this as any)[hiddenProperties][propertyKey];
|
||||||
|
},
|
||||||
|
set: function () {
|
||||||
|
throw new Error(`Cannot override property ${name}`);
|
||||||
|
},
|
||||||
|
enumerable: true,
|
||||||
|
configurable: false
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const LiveProperty = <T extends TouchBarItem<any>>(def: (config: T extends TouchBarItem<infer C> ? C : never) => any, onMutate?: (self: T, newValue: any) => void) => (target: T, propertyKey: keyof T) => {
|
||||||
|
extendConstructHook(target as any, function (this: T) {
|
||||||
|
(this as any)[hiddenProperties][propertyKey] = def((this as any)._config);
|
||||||
|
if (onMutate) onMutate((this as any), (this as any)[hiddenProperties][propertyKey]);
|
||||||
|
});
|
||||||
|
Object.defineProperty(target, propertyKey, {
|
||||||
|
get: function () {
|
||||||
|
return this[hiddenProperties][propertyKey];
|
||||||
|
},
|
||||||
|
set: function (value) {
|
||||||
|
if (onMutate) onMutate((this as any), value);
|
||||||
|
this[hiddenProperties][propertyKey] = value;
|
||||||
|
this.emit('change', this);
|
||||||
|
},
|
||||||
|
enumerable: true
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
abstract class TouchBarItem<ConfigType> extends EventEmitter {
|
||||||
|
@ImmutableProperty(() => `${nextItemID++}`) id!: string;
|
||||||
|
abstract type: string;
|
||||||
|
abstract onInteraction: Function | null;
|
||||||
|
child?: TouchBar;
|
||||||
|
|
||||||
|
private _parents: { id: string; type: string }[] = [];
|
||||||
|
private _config!: ConfigType;
|
||||||
|
|
||||||
|
constructor (config: ConfigType) {
|
||||||
|
super();
|
||||||
|
this._config = this._config || config || {} as any;
|
||||||
|
(this as any)[hiddenProperties] = {};
|
||||||
|
const hook = (this as any)._hook;
|
||||||
|
if (hook) hook.call(this);
|
||||||
|
delete (this as any)._hook;
|
||||||
|
}
|
||||||
|
|
||||||
|
public _addParent (item: TouchBarItem<any>) {
|
||||||
|
const existing = this._parents.some(test => test.id === item.id);
|
||||||
|
if (!existing) {
|
||||||
|
this._parents.push({
|
||||||
|
id: item.id,
|
||||||
|
type: item.type
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public _removeParent (item: TouchBarItem<any>) {
|
||||||
|
this._parents = this._parents.filter(test => test.id !== item.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TouchBarButton extends TouchBarItem<Electron.TouchBarButtonConstructorOptions> implements Electron.TouchBarButton {
|
||||||
|
@ImmutableProperty(() => 'button')
|
||||||
|
type!: string;
|
||||||
|
|
||||||
|
@LiveProperty<TouchBarButton>(config => config.label)
|
||||||
|
label!: string;
|
||||||
|
|
||||||
|
@LiveProperty<TouchBarButton>(config => config.accessibilityLabel)
|
||||||
|
accessibilityLabel!: string;
|
||||||
|
|
||||||
|
@LiveProperty<TouchBarButton>(config => config.backgroundColor)
|
||||||
|
backgroundColor!: string;
|
||||||
|
|
||||||
|
@LiveProperty<TouchBarButton>(config => config.icon)
|
||||||
|
icon!: Electron.NativeImage;
|
||||||
|
|
||||||
|
@LiveProperty<TouchBarButton>(config => config.iconPosition)
|
||||||
|
iconPosition!: Electron.TouchBarButton['iconPosition'];
|
||||||
|
|
||||||
|
@LiveProperty<TouchBarButton>(config => typeof config.enabled !== 'boolean' ? true : config.enabled)
|
||||||
|
enabled!: boolean;
|
||||||
|
|
||||||
|
@ImmutableProperty<TouchBarButton>(({ click: onClick }) => typeof onClick === 'function' ? () => onClick() : null)
|
||||||
|
onInteraction!: Function | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
class TouchBarColorPicker extends TouchBarItem<Electron.TouchBarColorPickerConstructorOptions> implements Electron.TouchBarColorPicker {
|
||||||
|
@ImmutableProperty(() => 'colorpicker')
|
||||||
|
type!: string;
|
||||||
|
|
||||||
|
@LiveProperty<TouchBarColorPicker>(config => config.availableColors)
|
||||||
|
availableColors!: string[];
|
||||||
|
|
||||||
|
@LiveProperty<TouchBarColorPicker>(config => config.selectedColor)
|
||||||
|
selectedColor!: string;
|
||||||
|
|
||||||
|
@ImmutableProperty<TouchBarColorPicker>(({ change: onChange }, setInternalProp) => typeof onChange === 'function' ? (details: { color: string }) => {
|
||||||
|
setInternalProp('selectedColor', details.color);
|
||||||
|
onChange(details.color);
|
||||||
|
} : null)
|
||||||
|
onInteraction!: Function | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
class TouchBarGroup extends TouchBarItem<Electron.TouchBarGroupConstructorOptions> implements Electron.TouchBarGroup {
|
||||||
|
@ImmutableProperty(() => 'group')
|
||||||
|
type!: string;
|
||||||
|
|
||||||
|
@LiveProperty<TouchBarGroup>(config => config.items instanceof TouchBar ? config.items : new TouchBar(config.items), (self, newChild: TouchBar) => {
|
||||||
|
if (self.child) {
|
||||||
|
for (const item of self.child.orderedItems) {
|
||||||
|
item._removeParent(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const item of newChild.orderedItems) {
|
||||||
|
item._addParent(item);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
child!: TouchBar;
|
||||||
|
|
||||||
|
onInteraction = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
class TouchBarLabel extends TouchBarItem<Electron.TouchBarLabelConstructorOptions> implements Electron.TouchBarLabel {
|
||||||
|
@ImmutableProperty(() => 'label')
|
||||||
|
type!: string;
|
||||||
|
|
||||||
|
@LiveProperty<TouchBarLabel>(config => config.label)
|
||||||
|
label!: string;
|
||||||
|
|
||||||
|
@LiveProperty<TouchBarLabel>(config => config.accessibilityLabel)
|
||||||
|
accessibilityLabel!: string;
|
||||||
|
|
||||||
|
@LiveProperty<TouchBarLabel>(config => config.textColor)
|
||||||
|
textColor!: string;
|
||||||
|
|
||||||
|
onInteraction = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
class TouchBarPopover extends TouchBarItem<Electron.TouchBarPopoverConstructorOptions> implements Electron.TouchBarPopover {
|
||||||
|
@ImmutableProperty(() => 'popover')
|
||||||
|
type!: string;
|
||||||
|
|
||||||
|
@LiveProperty<TouchBarPopover>(config => config.label)
|
||||||
|
label!: string;
|
||||||
|
|
||||||
|
@LiveProperty<TouchBarPopover>(config => config.icon)
|
||||||
|
icon!: Electron.NativeImage;
|
||||||
|
|
||||||
|
@LiveProperty<TouchBarPopover>(config => config.showCloseButton)
|
||||||
|
showCloseButton!: boolean;
|
||||||
|
|
||||||
|
@LiveProperty<TouchBarPopover>(config => config.items instanceof TouchBar ? config.items : new TouchBar(config.items), (self, newChild: TouchBar) => {
|
||||||
|
if (self.child) {
|
||||||
|
for (const item of self.child.orderedItems) {
|
||||||
|
item._removeParent(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const item of newChild.orderedItems) {
|
||||||
|
item._addParent(item);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
child!: TouchBar;
|
||||||
|
|
||||||
|
onInteraction = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
class TouchBarSlider extends TouchBarItem<Electron.TouchBarSliderConstructorOptions> implements Electron.TouchBarSlider {
|
||||||
|
@ImmutableProperty(() => 'slider')
|
||||||
|
type!: string;
|
||||||
|
|
||||||
|
@LiveProperty<TouchBarSlider>(config => config.label)
|
||||||
|
label!: string;
|
||||||
|
|
||||||
|
@LiveProperty<TouchBarSlider>(config => config.minValue)
|
||||||
|
minValue!: number;
|
||||||
|
|
||||||
|
@LiveProperty<TouchBarSlider>(config => config.maxValue)
|
||||||
|
maxValue!: number;
|
||||||
|
|
||||||
|
@LiveProperty<TouchBarSlider>(config => config.value)
|
||||||
|
value!: number;
|
||||||
|
|
||||||
|
@ImmutableProperty<TouchBarSlider>(({ change: onChange }, setInternalProp) => typeof onChange === 'function' ? (details: { value: number }) => {
|
||||||
|
setInternalProp('value', details.value);
|
||||||
|
onChange(details.value);
|
||||||
|
} : null)
|
||||||
|
onInteraction!: Function | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
class TouchBarSpacer extends TouchBarItem<Electron.TouchBarSpacerConstructorOptions> implements Electron.TouchBarSpacer {
|
||||||
|
@ImmutableProperty(() => 'spacer')
|
||||||
|
type!: string;
|
||||||
|
|
||||||
|
@ImmutableProperty<TouchBarSpacer>(config => config.size)
|
||||||
|
size!: Electron.TouchBarSpacer['size'];
|
||||||
|
|
||||||
|
onInteraction = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
class TouchBarSegmentedControl extends TouchBarItem<Electron.TouchBarSegmentedControlConstructorOptions> implements Electron.TouchBarSegmentedControl {
|
||||||
|
@ImmutableProperty(() => 'segmented_control')
|
||||||
|
type!: string;
|
||||||
|
|
||||||
|
@LiveProperty<TouchBarSegmentedControl>(config => config.segmentStyle)
|
||||||
|
segmentStyle!: Electron.TouchBarSegmentedControl['segmentStyle'];
|
||||||
|
|
||||||
|
@LiveProperty<TouchBarSegmentedControl>(config => config.segments || [])
|
||||||
|
segments!: Electron.SegmentedControlSegment[];
|
||||||
|
|
||||||
|
@LiveProperty<TouchBarSegmentedControl>(config => config.selectedIndex)
|
||||||
|
selectedIndex!: number;
|
||||||
|
|
||||||
|
@LiveProperty<TouchBarSegmentedControl>(config => config.mode)
|
||||||
|
mode!: Electron.TouchBarSegmentedControl['mode'];
|
||||||
|
|
||||||
|
@ImmutableProperty<TouchBarSegmentedControl>(({ change: onChange }, setInternalProp) => typeof onChange === 'function' ? (details: { selectedIndex: number, isSelected: boolean }) => {
|
||||||
|
setInternalProp('selectedIndex', details.selectedIndex);
|
||||||
|
onChange(details.selectedIndex, details.isSelected);
|
||||||
|
} : null)
|
||||||
|
onInteraction!: Function | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
class TouchBarScrubber extends TouchBarItem<Electron.TouchBarScrubberConstructorOptions> implements Electron.TouchBarScrubber {
|
||||||
|
@ImmutableProperty(() => 'scrubber')
|
||||||
|
type!: string;
|
||||||
|
|
||||||
|
@LiveProperty<TouchBarScrubber>(config => config.items)
|
||||||
|
items!: Electron.ScrubberItem[];
|
||||||
|
|
||||||
|
@LiveProperty<TouchBarScrubber>(config => config.selectedStyle || null)
|
||||||
|
selectedStyle!: Electron.TouchBarScrubber['selectedStyle'];
|
||||||
|
|
||||||
|
@LiveProperty<TouchBarScrubber>(config => config.overlayStyle || null)
|
||||||
|
overlayStyle!: Electron.TouchBarScrubber['overlayStyle'];
|
||||||
|
|
||||||
|
@LiveProperty<TouchBarScrubber>(config => config.showArrowButtons || false)
|
||||||
|
showArrowButtons!: boolean;
|
||||||
|
|
||||||
|
@LiveProperty<TouchBarScrubber>(config => config.mode || 'free')
|
||||||
|
mode!: Electron.TouchBarScrubber['mode'];
|
||||||
|
|
||||||
|
@LiveProperty<TouchBarScrubber>(config => typeof config.continuous === 'undefined' ? true : config.continuous)
|
||||||
|
continuous!: boolean;
|
||||||
|
|
||||||
|
@ImmutableProperty<TouchBarScrubber>(({ select: onSelect, highlight: onHighlight }) => typeof onSelect === 'function' || typeof onHighlight === 'function' ? (details: { type: 'select'; selectedIndex: number } | { type: 'highlight'; highlightedIndex: number }) => {
|
||||||
|
if (details.type === 'select') {
|
||||||
|
if (onSelect) onSelect(details.selectedIndex);
|
||||||
|
} else {
|
||||||
|
if (onHighlight) onHighlight(details.highlightedIndex);
|
||||||
|
}
|
||||||
|
} : null)
|
||||||
|
onInteraction!: Function | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
class TouchBarOtherItemsProxy extends TouchBarItem<null> implements Electron.TouchBarOtherItemsProxy {
|
||||||
|
@ImmutableProperty(() => 'other_items_proxy') type!: string;
|
||||||
|
onInteraction = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const escapeItemSymbol = Symbol('escape item');
|
||||||
|
|
||||||
|
class TouchBar extends EventEmitter implements Electron.TouchBar {
|
||||||
|
// Bind a touch bar to a window
|
||||||
|
static _setOnWindow (touchBar: TouchBar | Electron.TouchBarConstructorOptions['items'], window: Electron.BrowserWindow) {
|
||||||
|
if (window._touchBar != null) {
|
||||||
|
window._touchBar._removeFromWindow(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!touchBar) {
|
||||||
|
window._setTouchBarItems([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(touchBar)) {
|
||||||
|
touchBar = new TouchBar({ items: touchBar });
|
||||||
|
}
|
||||||
|
touchBar._addToWindow(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
private windowListeners: Record<number, Function> = {};
|
||||||
|
private items: Record<string, TouchBarItem<any>> = {};
|
||||||
|
orderedItems: TouchBarItem<any>[] = [];
|
||||||
|
|
||||||
|
constructor (options: Electron.TouchBarConstructorOptions) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
if (options == null) {
|
||||||
|
throw new Error('Must specify options object as first argument');
|
||||||
|
}
|
||||||
|
|
||||||
|
let { items, escapeItem } = options;
|
||||||
|
|
||||||
|
if (!Array.isArray(items)) {
|
||||||
|
items = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.windowListeners = {};
|
||||||
|
this.items = {};
|
||||||
|
this.escapeItem = (escapeItem as any) || null;
|
||||||
|
|
||||||
|
const registerItem = (item: TouchBarItem<any>) => {
|
||||||
|
this.items[item.id] = item;
|
||||||
|
item.on('change', this.changeListener);
|
||||||
|
if (item.child instanceof TouchBar) {
|
||||||
|
item.child.orderedItems.forEach(registerItem);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let hasOtherItemsProxy = false;
|
||||||
|
const idSet = new Set();
|
||||||
|
items.forEach((item) => {
|
||||||
|
if (!(item instanceof TouchBarItem)) {
|
||||||
|
throw new Error('Each item must be an instance of TouchBarItem');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.type === 'other_items_proxy') {
|
||||||
|
if (!hasOtherItemsProxy) {
|
||||||
|
hasOtherItemsProxy = true;
|
||||||
|
} else {
|
||||||
|
throw new Error('Must only have one OtherItemsProxy per TouchBar');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!idSet.has(item.id)) {
|
||||||
|
idSet.add(item.id);
|
||||||
|
} else {
|
||||||
|
throw new Error('Cannot add a single instance of TouchBarItem multiple times in a TouchBar');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// register in separate loop after all items are validated
|
||||||
|
for (const item of (items as TouchBarItem<any>[])) {
|
||||||
|
this.orderedItems.push(item);
|
||||||
|
registerItem(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private changeListener = (item: TouchBarItem<any>) => {
|
||||||
|
this.emit('change', item.id, item.type);
|
||||||
|
};
|
||||||
|
|
||||||
|
private [escapeItemSymbol]: TouchBarItem<unknown> | null = null;
|
||||||
|
|
||||||
|
set escapeItem (item: TouchBarItem<unknown> | null) {
|
||||||
|
if (item != null && !(item instanceof TouchBarItem)) {
|
||||||
|
throw new Error('Escape item must be an instance of TouchBarItem');
|
||||||
|
}
|
||||||
|
const escapeItem = this.escapeItem;
|
||||||
|
if (escapeItem) {
|
||||||
|
escapeItem.removeListener('change', this.changeListener);
|
||||||
|
}
|
||||||
|
this[escapeItemSymbol] = item;
|
||||||
|
if (this.escapeItem != null) {
|
||||||
|
this.escapeItem.on('change', this.changeListener);
|
||||||
|
}
|
||||||
|
this.emit('escape-item-change', item);
|
||||||
|
}
|
||||||
|
|
||||||
|
get escapeItem (): TouchBarItem<unknown> | null {
|
||||||
|
return this[escapeItemSymbol];
|
||||||
|
}
|
||||||
|
|
||||||
|
_addToWindow (window: Electron.BrowserWindow) {
|
||||||
|
const { id } = window;
|
||||||
|
|
||||||
|
// Already added to window
|
||||||
|
if (Object.prototype.hasOwnProperty.call(this.windowListeners, id)) return;
|
||||||
|
|
||||||
|
window._touchBar = this;
|
||||||
|
|
||||||
|
const changeListener = (itemID: string) => {
|
||||||
|
window._refreshTouchBarItem(itemID);
|
||||||
|
};
|
||||||
|
this.on('change', changeListener);
|
||||||
|
|
||||||
|
const escapeItemListener = (item: Electron.TouchBarItemType | null) => {
|
||||||
|
window._setEscapeTouchBarItem(item != null ? item : {});
|
||||||
|
};
|
||||||
|
this.on('escape-item-change', escapeItemListener);
|
||||||
|
|
||||||
|
const interactionListener = (_: any, itemID: string, details: any) => {
|
||||||
|
let item = this.items[itemID];
|
||||||
|
if (item == null && this.escapeItem != null && this.escapeItem.id === itemID) {
|
||||||
|
item = this.escapeItem;
|
||||||
|
}
|
||||||
|
if (item != null && item.onInteraction != null) {
|
||||||
|
item.onInteraction(details);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
window.on('-touch-bar-interaction', interactionListener);
|
||||||
|
|
||||||
|
const removeListeners = () => {
|
||||||
|
this.removeListener('change', changeListener);
|
||||||
|
this.removeListener('escape-item-change', escapeItemListener);
|
||||||
|
window.removeListener('-touch-bar-interaction', interactionListener);
|
||||||
|
window.removeListener('closed', removeListeners);
|
||||||
|
window._touchBar = null;
|
||||||
|
delete this.windowListeners[id];
|
||||||
|
const unregisterItems = (items: TouchBarItem<any>[]) => {
|
||||||
|
for (const item of items) {
|
||||||
|
item.removeListener('change', this.changeListener);
|
||||||
|
if (item.child instanceof TouchBar) {
|
||||||
|
unregisterItems(item.child.orderedItems);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
unregisterItems(this.orderedItems);
|
||||||
|
if (this.escapeItem) {
|
||||||
|
this.escapeItem.removeListener('change', this.changeListener);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
window.once('closed', removeListeners);
|
||||||
|
this.windowListeners[id] = removeListeners;
|
||||||
|
|
||||||
|
window._setTouchBarItems(this.orderedItems);
|
||||||
|
escapeItemListener(this.escapeItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
_removeFromWindow (window: Electron.BrowserWindow) {
|
||||||
|
const removeListeners = this.windowListeners[window.id];
|
||||||
|
if (removeListeners != null) removeListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
static TouchBarButton = TouchBarButton;
|
||||||
|
static TouchBarColorPicker = TouchBarColorPicker;
|
||||||
|
static TouchBarGroup = TouchBarGroup;
|
||||||
|
static TouchBarLabel = TouchBarLabel;
|
||||||
|
static TouchBarPopover = TouchBarPopover;
|
||||||
|
static TouchBarSlider = TouchBarSlider;
|
||||||
|
static TouchBarSpacer = TouchBarSpacer;
|
||||||
|
static TouchBarSegmentedControl = TouchBarSegmentedControl;
|
||||||
|
static TouchBarScrubber = TouchBarScrubber;
|
||||||
|
static TouchBarOtherItemsProxy = TouchBarOtherItemsProxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TouchBar;
|
|
@ -19,6 +19,17 @@ declare namespace Electron {
|
||||||
setAppPath(path: string | null): void;
|
setAppPath(path: string | null): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TouchBarItemType = NonNullable<Electron.TouchBarConstructorOptions['items']>[0];
|
||||||
|
|
||||||
|
interface BrowserWindow {
|
||||||
|
_touchBar: Electron.TouchBar | null;
|
||||||
|
_setTouchBarItems: (items: TouchBarItemType[]) => void;
|
||||||
|
_setEscapeTouchBarItem: (item: TouchBarItemType | {}) => void;
|
||||||
|
_refreshTouchBarItem: (itemID: string) => void;
|
||||||
|
on(event: '-touch-bar-interaction', listener: (event: Event, itemID: string, details: any) => void): this;
|
||||||
|
removeListener(event: '-touch-bar-interaction', listener: (event: Event, itemID: string, details: any) => void): this;
|
||||||
|
}
|
||||||
|
|
||||||
interface ContextBridge {
|
interface ContextBridge {
|
||||||
internalContextBridge: {
|
internalContextBridge: {
|
||||||
contextIsolationEnabled: boolean;
|
contextIsolationEnabled: boolean;
|
||||||
|
@ -29,6 +40,10 @@ declare namespace Electron {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface TouchBar {
|
||||||
|
_removeFromWindow: (win: BrowserWindow) => void;
|
||||||
|
}
|
||||||
|
|
||||||
interface WebContents {
|
interface WebContents {
|
||||||
_getURL(): string;
|
_getURL(): string;
|
||||||
_loadURL(url: string, options: Electron.LoadURLOptions): void;
|
_loadURL(url: string, options: Electron.LoadURLOptions): void;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче