Adding registers panes of CPU window

This commit is contained in:
2021-09-02 00:16:22 +00:00
parent 3dc8b93f94
commit ff07e1907f
32 changed files with 2437 additions and 444 deletions
-1
View File
@@ -200,7 +200,6 @@ Toolkit.Application = class Application extends Toolkit.Panel {
// A pointer or mouse down even has propagated
onpropagation(e) {
e.preventDefault();
e.stopPropagation();
for (let listener of this.propagationListeners)
listener(e, this);
+21 -22
View File
@@ -45,43 +45,51 @@ Toolkit.Button = class Button extends Toolkit.Component {
this.clickListeners.push(listener);
}
// The button was activated
click(e) {
if (!this.enabled)
return;
for (let listener of this.clickListeners)
listener(e);
}
// Request focus on the appropriate element
focus() {
this.element.focus();
}
// Retrieve the button's accessible name
// Retrieve the component's accessible name
getName() {
return this.name;
}
// Retrieve the button's display text
// Retrieve the component's display text
getText() {
return this.text;
}
// Retrieve the button's tool tip text
// Retrieve the component's tool tip text
getToolTip() {
return this.toolTip;
}
// Determine whether the button is enabled
// Determine whether the component is enabled
isEnabled() {
return this.enabled;
}
// Determine whether the button is focusable
// Determine whether the component is focusable
isFocusable() {
return this.focusable;
}
// Specify whether the button is enabled
// Specify whether the component is enabled
setEnabled(enabled) {
this.enabled = enabled = !!enabled;
this.element.setAttribute("aria-disabled", !enabled);
}
// Specify whether the button can receive focus
// Specify whether the component can receive focus
setFocusable(focusable) {
this.focusable = focusable = !!focusable;
if (focusable)
@@ -89,19 +97,19 @@ Toolkit.Button = class Button extends Toolkit.Component {
else this.element.removeAttribute("tabindex");
}
// Specify the button's accessible name
// Specify the component's accessible name
setName(name) {
this.name = name || "";
this.localize();
}
// Specify the button's display text
// Specify the component's display text
setText(text) {
this.text = text || "";
this.localize();
}
// Specify the button's tool tip text
// Specify the component's tool tip text
setToolTip(toolTip) {
this.toolTip = toolTip || "";
this.localize();
@@ -133,14 +141,6 @@ Toolkit.Button = class Button extends Toolkit.Component {
///////////////////////////// Private Methods /////////////////////////////
// The button was activated
activate(e) {
if (!this.enabled)
return;
for (let listener of this.clickListeners)
listener(e, this);
}
// Key down event handler
onkeydown(e) {
@@ -152,7 +152,7 @@ Toolkit.Button = class Button extends Toolkit.Component {
switch (e.key) {
case " ":
case "Enter":
this.activate(e);
this.click(e);
break;
default: return;
}
@@ -166,7 +166,6 @@ Toolkit.Button = class Button extends Toolkit.Component {
onpointerdown(e) {
// Configure event
e.preventDefault();
e.stopPropagation();
// Configure focus
@@ -221,11 +220,11 @@ Toolkit.Button = class Button extends Toolkit.Component {
// Configure element
this.element.releasePointerCapture(e.pointerId);
// Activate the menu item if it is active
// Activate the component if it is active
if (!this.element.hasAttribute("active"))
return;
this.element.removeAttribute("active");
this.activate(e);
this.click(e);
}
};
+44
View File
@@ -0,0 +1,44 @@
"use strict";
// Grouping manager for mutually-exclusive controls
Toolkit.ButtonGroup = class ButtonGroup {
// Object constructor
constructor() {
this.components = [];
}
///////////////////////////// Public Methods //////////////////////////////
// Add a component to the group
add(component) {
if (this.components.indexOf(component) != -1)
return component;
this.components.push(component);
if ("setGroup" in component)
component.setGroup(this);
return component;
}
// Select only one button in the group
setChecked(component) {
for (let comp of this.components) {
if ("setChecked" in comp)
comp.setChecked(comp == component, this);
}
}
// Remove a component from the group
remove(component) {
let index = this.components.indexOf(component);
if (index == -1)
return false;
this.components.splice(index, 1);
if ("setGroup" in component)
component.setGroup(null);
return true;
}
};
+190
View File
@@ -0,0 +1,190 @@
"use strict";
// On/off toggle checkbox
Toolkit.CheckBox = class CheckBox extends Toolkit.Panel {
// Object constructor
constructor(application, options) {
super(application, options);
options = options || {};
// Configure instance fields
this.changeListeners = [];
this.checked = false;
this.enabled = "enabled" in options ? !!options.enabled : true;
this.text = options.text || "";
// Configure element
this.setLayout("grid", {
columns : "max-content max-content"
});
this.setDisplay("inline-grid");
this.setHollow(false);
this.setOverflow("visible", "visible");
this.element.setAttribute("tabindex", "0");
this.element.setAttribute("role", "checkbox");
this.element.setAttribute("aria-checked", "false");
this.element.style.alignItems = "center";
this.element.addEventListener("keydown" , e=>this.onkeydown (e));
this.element.addEventListener("pointerdown", e=>this.onpointerdown(e));
this.element.addEventListener("pointermove", e=>this.onpointermove(e));
this.element.addEventListener("pointerup" , e=>this.onpointerup (e));
// Configure check box
this.check = this.add(this.newLabel());
this.check.element.setAttribute("name", "check");
this.check.element.setAttribute("aria-hidden", "true");
// Configure label
this.label = this.add(this.newLabel({ localized: true }));
this.label.element.setAttribute("name", "label");
this.element.setAttribute("aria-labelledby", this.label.id);
// Configure properties
this.setChecked(options.checked);
this.setEnabled(this.enabled);
this.setText (this.text );
}
///////////////////////////// Public Methods //////////////////////////////
// Add a callback for change events
addChangeListener(listener) {
if (this.changeListeners.indexOf(listener) == -1)
this.changeListeners.push(listener);
}
// Request focus on the appropriate element
focus() {
this.element.focus();
}
// Determine whether the component is checked
isChecked() {
return this.checked;
}
// Determine whether the component is enabled
isEnabled() {
return this.enabled;
}
// Specify whether the component is checked
setChecked(checked, e) {
checked = !!checked;
if (checked == this.checked)
return;
this.checked = checked;
this.element.setAttribute("aria-checked", checked);
if (e === undefined)
return;
for (let listener of this.changeListeners)
listener(e);
}
// Specify whether the component is enabled
setEnabled(enabled) {
this.enabled = enabled = !!enabled;
this.element.setAttribute("aria-disabled", !enabled);
if (enabled)
this.element.setAttribute("tabindex", "0");
else this.element.removeAttribute("tabindex");
}
// Specify the component's display text
setText(text) {
this.text = text = text || "";
this.label.setText(text);
}
///////////////////////////// Private Methods /////////////////////////////
// Key down event handler
onkeydown(e) {
// Error checking
if (!this.enabled)
return;
// Ignore the key
if (e.key != " ")
return;
// Configure event
e.preventDefault();
e.stopPropagation();
// Toggle the checked state
this.setChecked(!this.checked, e);
}
// Pointer down event handler
onpointerdown(e) {
// Configure event
//e.preventDefault();
e.stopPropagation();
// Configure focus
if (this.enabled)
this.focus();
else return;
// Error checking
if (e.button != 0 || this.element.hasPointerCapture(e.captureId))
return;
// Configure element
this.element.setPointerCapture(e.pointerId);
this.element.setAttribute("active", "");
}
// Pointer move event handler
onpointermove(e) {
// Error checking
if (!this.element.hasPointerCapture(e))
return;
// Configure event
e.preventDefault();
e.stopPropagation();
// Working variables
let bounds = this.getBounds();
let active =
e.x >= bounds.x && e.x < bounds.x + bounds.width &&
e.y >= bounds.y && e.y < bounds.y + bounds.height
;
// Configure element
if (active)
this.element.setAttribute("active", "");
else this.element.removeAttribute("active");
}
// Pointer up event handler
onpointerup(e) {
// Configure event
e.stopPropagation();
// Error checking
if (!this.element.hasPointerCapture(e.pointerId))
return;
// Configure element
this.element.releasePointerCapture(e.pointerId);
// Activate the component if it is active
if (!this.element.hasAttribute("active"))
return;
this.element.removeAttribute("active");
this.setChecked(!this.checked, e);
}
};
+34 -16
View File
@@ -21,6 +21,10 @@ Toolkit.Component = class Component {
// Configure component
this.element.component = this;
this.setSize(
"width" in options ? options.width : null,
"height" in options ? options.height : null
);
this.setVisible(this.visible);
}
@@ -58,11 +62,11 @@ Toolkit.Component = class Component {
}
// Specify the location and size of the component
setBounds(left, top, width, height) {
setBounds(left, top, width, height, minimum) {
this.setLeft (left );
this.setTop (top );
this.setWidth (width );
this.setHeight(height);
this.setWidth (width , minimum);
this.setHeight(height, minimum);
}
// Specify the display CSS property of the visible element
@@ -71,12 +75,19 @@ Toolkit.Component = class Component {
this.setVisible(this.visible);
}
// Specify the height of the element
setHeight(height) {
if (height === null)
// Specify the height of the component
setHeight(height, minimum) {
if (height === null) {
this.element.style.removeProperty("min-height");
this.element.style.removeProperty("height");
else this.element.style.height =
typeof height == "number" ? height + "px" : height
} else {
height = typeof height == "number" ?
Math.max(0, height) + "px" : height;
this.element.style.height = height;
if (minimum)
this.element.style.minHeight = height;
else this.element.style.removeProperty("min-height");
}
}
// Specify the horizontal position of the component
@@ -100,9 +111,9 @@ Toolkit.Component = class Component {
}
// Specify both the width and the height of the component
setSize(width, height) {
this.setHeight(height);
this.setWidth (width );
setSize(width, height, minimum) {
this.setHeight(height, minimum);
this.setWidth (width , minimum);
}
// Specify the vertical position of the component
@@ -123,12 +134,19 @@ Toolkit.Component = class Component {
} else this.element.style.display = "none";
}
// Specify the width of the element
setWidth(width) {
if (width === null)
// Specify the width of the component
setWidth(width, minimum) {
if (width === null) {
this.element.style.removeProperty("min-width");
this.element.style.removeProperty("width");
else this.element.style.width =
typeof width == "number" ? width + "px" : width ;
} else {
width = typeof width == "number" ?
Math.max(0, width) + "px" : width;
this.element.style.width = width;
if (minimum)
this.element.style.minWidth = width;
else this.element.style.removeProperty("min-width");
}
}
+30 -2
View File
@@ -5,10 +5,11 @@ Toolkit.Label = class Label extends Toolkit.Component {
// Object constructor
constructor(application, options) {
super(application, "div", options);
super(application, options&&options.label ? "label" : "div", options);
options = options || {};
// Configure instance fields
this.focusable = "focusable" in options ? !!options.focusable : false;
this.localized = "localized" in options ? !!options.localized : false;
this.text = options.text || "";
@@ -17,7 +18,8 @@ Toolkit.Label = class Label extends Toolkit.Component {
this.element.style.userSelect = "none";
// Configure properties
this.setText(this.text);
this.setFocusable(this.focusable);
this.setText (this.text);
if (this.localized)
this.application.addComponent(this);
}
@@ -26,17 +28,43 @@ Toolkit.Label = class Label extends Toolkit.Component {
///////////////////////////// Public Methods //////////////////////////////
// Request focus on the appropriate element
focus() {
if (this.focusable)
this.element.focus();
}
// Retrieve the label's display text
getText() {
return this.text;
}
// Determine whether the component is focusable
isFocusable() {
return this.focusable;
}
// Specify the label's display text
setText(text) {
this.text = text || "";
this.localize();
}
// Specify whether the component is focusable
setFocusable(focusable) {
this.focusable = focusable = !!focusable;
if (focusable) {
this.element.setAttribute("tabindex", "0");
this.localized && this.application &&
this.application.addComponent(this);
} else {
this.element.removeAttribute("aria-label");
this.element.removeAttribute("tabindex");
this.localized && this.application &&
this.application.removeComponent(this);
}
}
///////////////////////////// Package Methods /////////////////////////////
+1 -1
View File
@@ -15,7 +15,7 @@ Toolkit.MenuBar = class MenuBar extends Toolkit.Panel {
// Configure element
this.element.style.position = "relative";
this.element.style.zIndex = "1";
this.element.style.zIndex = "2";
this.element.setAttribute("role", "menubar");
this.setLayout("flex", {
direction: "row",
+96 -14
View File
@@ -15,22 +15,23 @@ Toolkit.Panel = class Panel extends Toolkit.Component {
this.children = [];
this.columns = null;
this.direction = "row";
this.focusable = "focusable" in options ? !!options.focusable:false;
this.hollow = "hollow" in options ? !!options.hollow : true;
this.layout = null;
this.name = options.name || "";
this.overflowX = options.overflowX || "hidden";
this.overflowY = options.overflowY || "hidden";
this.rows = null;
this.wrap = false;
// Configure element
if ("noShrink" in options ? !options.noShrink : true) {
this.element.style.minHeight = "0";
this.element.style.minWidth = "0";
}
this.setOverflow(this.overflowX, this.overflowY);
// Configure layout
options = options || {};
this.setLayout(options.layout || null, options);
// Configure properties
this.setFocusable(this.focusable);
this.setHollow (this.hollow);
this.setLayout (options.layout || null, options);
this.setName (this.name);
this.setOverflow (this.overflowX, this.overflowY);
if (this.application && this.focusable)
this.application.addComponent(this);
}
@@ -54,11 +55,37 @@ Toolkit.Panel = class Panel extends Toolkit.Component {
return component;
}
// Request focus on the appropriate element
focus() {
if (this.focusable)
this.element.focus();
}
// Retrieve the component's accessible name
getName() {
return this.name;
}
// Determine whether the component is focusable
isFocusable() {
return this.focusable;
}
// Determine whether the component is hollow
isHollow() {
return this.hollow;
}
// Create a Button and associate it with the application
newButton(options) {
return new Toolkit.Button(this.application, options);
}
// Create a CheckBox and associate it with the application
newCheckBox(options) {
return new Toolkit.CheckBox(this.application, options);
}
// Create a Label and associate it with the application
newLabel(options) {
return new Toolkit.Label(this.application, options);
@@ -74,6 +101,21 @@ Toolkit.Panel = class Panel extends Toolkit.Component {
return new Toolkit.Panel(this.application, options);
}
// Create a RadioButton and associate it with the application
newRadioButton(options) {
return new Toolkit.RadioButton(this.application, options);
}
// Create a Splitter and associate it with the application
newSplitter(options) {
return new Toolkit.Splitter(this.application, options);
}
// Create a TextBox and associate it with the application
newTextBox(options) {
return new Toolkit.TextBox(this.application, options);
}
// Create a Window and associate it with the application
newWindow(options) {
return new Toolkit.Window(this.application, options);
@@ -115,6 +157,31 @@ Toolkit.Panel = class Panel extends Toolkit.Component {
this.children.splice(index, 1);
}
// Specify whether the component is focusable
setFocusable(focusable) {
this.focusable = focusable = !!focusable;
if (focusable) {
this.element.setAttribute("tabindex", "0");
this.application && this.application.addComponent(this);
} else {
this.element.removeAttribute("aria-label");
this.element.removeAttribute("tabindex");
this.application && this.application.removeComponent(this);
}
}
// Specify whether the component is hollow
setHollow(hollow) {
this.hollow = hollow = !!hollow;
if (hollow) {
this.element.style.minHeight = "0";
this.element.style.minWidth = "0";
} else {
this.element.style.removeProperty("min-height");
this.element.style.removeProperty("min-width" );
}
}
// Configure the element's layout
setLayout(layout, options) {
@@ -122,6 +189,7 @@ Toolkit.Panel = class Panel extends Toolkit.Component {
this.layout = layout;
// Processing by layout
options = options || {};
switch (layout) {
case "block" : this.setBlockLayout (options); break;
case "desktop": this.setDesktopLayout(options); break;
@@ -132,11 +200,17 @@ Toolkit.Panel = class Panel extends Toolkit.Component {
}
// Specify the component's accessible name
setName(name) {
this.name = name || "";
if (this.focusable)
this.localize();
}
// Configure the panel's overflow scrolling behavior
setOverflow(x, y) {
this.overflowX = x || "hidden";
this.overflowY = y || "hidden";
this.element.style.overflow = this.overflowX + " " + this.overflowY;
this.element.style.overflowX = this.overflowX = x || "hidden";
this.element.style.overflowY = this.overflowY = y || this.overflowX;
}
// Specify the semantic role of the panel
@@ -153,7 +227,15 @@ Toolkit.Panel = class Panel extends Toolkit.Component {
// Move a window to the foreground
bringToFront(wnd) {
for (let child of this.children)
child.element.style.zIndex = child == wnd ? "0" : "1";
child.element.style.zIndex = child == wnd ? "1" : "0";
}
// Update display text with localized strings
localize() {
let name = this.name;
if (this.application)
name = this.application.translate(name, this);
this.element.setAttribute("aria-label", name);
}
+59
View File
@@ -0,0 +1,59 @@
"use strict";
// Select-only radio button
Toolkit.RadioButton = class RadioButton extends Toolkit.CheckBox {
// Object constructor
constructor(application, options) {
super(application, options);
options = options || {};
// Configure instance fields
this.group = null;
// Configure element
this.element.setAttribute("role", "radio");
// Configure properties
this.setGroup(options.group || null);
}
///////////////////////////// Public Methods //////////////////////////////
// Retrieve the enclosing ButtonGroup
getGroup() {
return this.group;
}
// Specify whether the component is checked (overrides superclass)
setChecked(checked, e) {
checked = !!checked;
if (e instanceof Event && !checked || checked == this.checked)
return;
this.checked = checked;
this.element.setAttribute("aria-checked", checked);
if (this.group != null && e != this.group)
this.group.setChecked(this);
if (e === undefined)
return;
for (let listener of this.changeListeners)
listener(e);
}
// Specify the enclosing ButtonGroup
setGroup(group) {
group = group || null;
if (group == this.group)
return;
if (this.group != null)
this.group.remove(this);
this.group = group;
if (group != null)
group.add(this);
}
};
+301
View File
@@ -0,0 +1,301 @@
"use strict";
// Interactive splitter
Toolkit.Splitter = class Splitter extends Toolkit.Component {
// Object constructor
constructor(application, options) {
super(application, "div", options);
options = options || {};
// Configure instance fields
this.component = options.component || null;
this.dragPointer = null;
this.dragPos = 0;
this.dragSize = 0;
this.orientation = options.orientation || "horizontal";
this.name = options.name || "";
this.edge = options.edge ||
(this.orientation == "horizontal" ? "top" : "left");
// Configure element
this.element.setAttribute("role" , "separator");
this.element.setAttribute("tabindex" , "0");
this.element.setAttribute("aria-valuemin", "0");
this.element.addEventListener("keydown" , e=>this.onkeydown (e));
this.element.addEventListener("pointerdown", e=>this.onpointerdown(e));
this.element.addEventListener("pointermove", e=>this.onpointermove(e));
this.element.addEventListener("pointerup" , e=>this.onpointerup (e));
// Configure properties
this.setComponent (this.component );
this.setName (this.name );
this.setOrientation(this.orientation);
this.application.addComponent(this);
}
///////////////////////////// Public Methods //////////////////////////////
// Request focus on the appropriate element
focus() {
this.element.focus();
}
// Retrieve the component managed by this Splitter
getComponent() {
return this.component;
}
// Retrieve the component's accessible name
getName() {
return this.name;
}
// Retrieve the component's orientation
getOrientation() {
return this.orientation;
}
// Determine the current and maximum separator values
measure() {
let max = 0;
let now = 0;
// Mesure the component
if (this.component != null && this.parent != null) {
let component = this.component.getBounds();
let bounds = this.getBounds();
let panel = this.parent.getBounds();
// Horizontal Splitter
if (this.orientation == "horizontal") {
max = panel.height - bounds.height;
now = Math.max(0, Math.min(max, component.height));
this.component.setSize(null, now);
}
// Vertical Splitter
else {
max = panel.width - bounds.width;
now = Math.max(0, Math.min(max, component.width));
this.component.setSize(now, null);
}
}
// Configure element
this.element.setAttribute("aria-valuemax", max);
this.element.setAttribute("aria-valuenow", now);
}
// Specify the component managed by this Splitter
setComponent(component) {
this.component = component = component || null;
this.element.setAttribute("aria-controls",
component == null ? "" : component.id);
this.measure();
}
// Specify the component's accessible name
setName(name) {
this.name = name || "";
this.localize();
}
// Specify the component's orientation
setOrientation(orientation) {
switch (orientation) {
case "horizontal":
this.orientation = "horizontal";
this.setSize(null, 3, true);
this.element.setAttribute("aria-orientation", "horizontal");
this.element.style.cursor = "ew-resize";
break;
case "vertical":
this.orientation = "vertical";
this.setSize(3, null, true);
this.element.setAttribute("aria-orientation", "vertical");
this.element.style.cursor = "ns-resize";
}
this.measure();
}
///////////////////////////// Package Methods /////////////////////////////
// Update display text with localized strings
localize() {
let name = this.name;
if (this.application) {
name = this.application.translate(name, this);
}
this.element.setAttribute("aria-label", name);
}
///////////////////////////// Private Methods /////////////////////////////
// Key press event handler
onkeydown(e) {
// Error checking
if (this.component == null)
return;
let pos = this.component.getBounds();
let size = this .getBounds();
let max = this.parent .getBounds();
if (this.orientation == "horizontal") {
max = max .height;
pos = pos .height;
size = size.height;
} else {
max = max .width;
pos = pos .width;
size = size.width;
}
if (this.edge == "top" || this.edge == "left")
max -= size;
// Processing by key
if (this.component != null) switch (e.key) {
case "ArrowDown":
switch (this.edge) {
case "bottom":
this.component.setSize(null, Math.min(max, pos - 6));
break;
case "top":
this.component.setSize(null, Math.min(max, pos + 6));
}
break;
case "ArrowLeft":
switch (this.edge) {
case "left":
this.component.setSize(Math.min(max, pos - 6), null);
break;
case "right":
this.component.setSize(Math.min(max, pos + 6), null);
}
break;
case "ArrowRight":
switch (this.edge) {
case "left":
this.component.setSize(Math.min(max, pos + 6), null);
break;
case "right":
this.component.setSize(Math.min(max, pos - 6), null);
}
break;
case "ArrowUp":
switch (this.edge) {
case "bottom":
this.component.setSize(null, Math.min(max, pos + 6));
break;
case "top":
this.component.setSize(null, Math.min(max, pos - 6));
}
break;
case "Escape":
if (this.dragPointer === null)
return;
this.element.releasePointerCapture(this.dragPointer);
this.dragPointer = null;
if (this.orientation == "horizontal")
this.component.setHeight(null, this.dragSize);
else this.component.setWidth(this.dragSize, null);
break;
default: return;
}
// Configure event
e.preventDefault();
e.stopPropagation();
}
// Pointer down event handler
onpointerdown(e) {
// Request focus
this.focus();
// Configure event
e.stopPropagation();
// Error checking
if (
this.component == null ||
e.button != 0 ||
this.element.hasPointerCapture(e.pointerId)
) return;
// Capture the pointer
this.element.setPointerCapture(e.pointerId);
this.dragPointer = e.pointerId;
let bounds = this.component.getBounds();
if (this.orientation == "horizontal") {
this.dragPos = e.y;
this.dragSize = bounds.height;
} else {
this.dragPos = e.x;
this.dragSize = bounds.width;
}
}
// Pointer move event handler
onpointermove(e) {
// Configure event
e.preventDefault();
e.stopPropagation();
// Error checking
if (
this.component == null ||
!this.element.hasPointerCapture(e.pointerId)
) return;
// Resize the component
let bounds = this.getBounds();
let panel = this.parent.getBounds();
switch (this.edge) {
case "bottom":
this.component.setSize(null, Math.max(0, Math.min(
this.dragSize - e.y + this.dragPos,
panel.height - bounds.height
)));
break;
case "left":
this.component.setSize(Math.max(0, Math.min(
this.dragSize + e.x - this.dragPos,
panel.width - bounds.width
)), null);
break;
case "right":
this.component.setSize(Math.max(0, Math.min(
this.dragSize - e.x + this.dragPos,
panel.width - bounds.width
)), null);
break;
case "top":
this.component.setSize(null, Math.max(0, Math.min(
this.dragSize + e.y - this.dragPos,
panel.height - bounds.height
)));
break;
}
this.measure();
}
// Pointer up event handler
onpointerup(e) {
e.preventDefault();
e.stopPropagation();
this.element.releasePointerCapture(e.pointerId);
this.dragPointer = null;
}
};
+138
View File
@@ -0,0 +1,138 @@
"use strict";
Toolkit.TextBox = class TextBox extends Toolkit.Component {
constructor(application, options) {
super(application, "input", options);
options = options || {};
// Configure instance fields
this.changeListeners = [];
this.commitListeners = [];
this.enabled = "enabled" in options ? !!options.enabled : true;
this.name = options.name || "";
this.lastCommit = "";
// Configure element
this.element.size = 1;
this.element.type = "text";
this.element.addEventListener("blur" , e=>this.commit (e));
this.element.addEventListener("input" , e=>this.onchange (e));
this.element.addEventListener("keydown", e=>this.onkeydown(e));
// Configure properties
this.setEnabled(this.enabled);
this.setName (this.name );
this.setText (options.text || "");
this.application.addComponent(this);
}
///////////////////////////// Public Methods //////////////////////////////
// Add a callback for change events
addChangeListener(listener) {
if (this.changeListeners.indexOf(listener) == -1)
this.changeListeners.push(listener);
}
// Add a callback for commit events
addCommitListener(listener) {
if (this.commitListeners.indexOf(listener) == -1)
this.commitListeners.push(listener);
}
// Request focus on the appropriate element
focus() {
this.element.focus();
}
// Retrieve the component's accessible name
getName() {
return this.name;
}
// Retrieve the component's display text
getText() {
return this.element.value;
}
// Determine whether the component is enabled
isEnabled() {
return this.enabled;
}
// Specify whether the component is enabled
setEnabled(enabled) {
this.enabled = enabled = !!enabled;
this.element.setAttribute("aria-disabled", !enabled);
if (enabled)
this.element.removeAttribute("disabled");
else this.element.setAttribute("disabled", "");
}
// Specify the component's accessible name
setName(name) {
this.name = name || "";
this.localize();
}
// Specify the component's display text
setText(text) {
text = !text && text !== 0 ? "" : "" + text;
this.lastCommit = text;
this.element.value = text;
}
///////////////////////////// Package Methods /////////////////////////////
// Update display text with localized strings
localize() {
let name = this.name;
if (this.application)
name = this.application.translate(name, this);
this.element.setAttribute("aria-label", name);
}
///////////////////////////// Private Methods /////////////////////////////
// Input finalized
commit(e) {
let text = this.element.value || "";
if (!this.enabled || text == this.lastCommit)
return;
this.lastCommit = text;
for (let listener of this.commitListeners)
listener(e, this);
}
// Text changed event handler
onchange(e) {
e.stopPropagation();
if (!this.enabled)
return;
for (let listener of this.changeListeners)
listener(e, this);
}
// Key press event handler
onkeydown(e) {
// Configure event
e.stopPropagation();
// Error checking
if (!this.enabled)
return;
// The Enter key was pressed
if (e.key == "Enter")
this.commit(e);
}
};
+102 -143
View File
@@ -11,28 +11,22 @@ Toolkit.Window = class Window extends Toolkit.Panel {
// Configure instance fields
this.closeListeners = [];
this.dragBounds = null;
this.dragClient = null;
this.dragCursor = { x: 0, y: 0 };
this.dragEdge = null;
this.dragPointer = null;
this.initialCenter = "center" in options ? !!options.center : false;
this.initialHeight = options.height || 64;
this.initialWidth = options.width || 64;
this.lastFocus = this.element;
this.shown = this.visible;
this.title = options.title || "";
// Configure element
this.setLayout("flex", {
alignCross: "stretch",
direction : "column",
overflowX : "visible",
overflowY : "visible"
});
this.setLayout("grid", { columns: "auto" });
this.setRole("dialog");
this.setBounds(0, 0, 64, 64);
this.setLocation(0, 0);
this.element.style.position = "absolute";
this.element.setAttribute("aria-modal", "false");
this.element.setAttribute("focus" , "false");
this.element.setAttribute("tabindex" , "-1" );
this.element.setAttribute("tabindex" , "0" );
this.element.addEventListener(
"blur" , e=>this.onblur (e), { capture: true });
this.element.addEventListener(
@@ -42,69 +36,48 @@ Toolkit.Window = class Window extends Toolkit.Panel {
this.element.addEventListener("pointermove", e=>this.onpointermove(e));
this.element.addEventListener("pointerup" , e=>this.onpointerup (e));
// Configure body container
// Primary visible container
this.body = this.add(this.newPanel({
layout : "flex",
alignCross: "stretch",
direction : "column",
overflowX : "visible",
overflowY : "visible"
layout : "grid",
overflowX: "visible",
overflowY: "visible",
rows : "max-content auto"
}));
this.body.element.style.flexGrow = "1";
this.body.element.style.margin = "3px";
this.body.element.setAttribute("name", "body");
// Configure title bar
// Title bar
this.titleBar = this.body.add(this.newPanel({
layout : "flex",
alignCross: "center",
direction : "row",
noShrink : true,
overflowX : "visible",
overflowY : "visible"
layout : "grid",
columns: "max-content auto max-content",
hollow : false
}));
this.titleBar.element.setAttribute("name", "title-bar");
// Configure title icon element
this.titleIcon = this.titleBar.add(this.newPanel({}));
this.titleIcon.element.setAttribute("name", "title-icon");
this.titleIcon.element.style.removeProperty("min-width");
// Title bar icon
this.titleIcon = this.titleBar.add(this.newPanel());
this.titleIcon.element.setAttribute("name", "icon");
this.titleIcon.element.setAttribute("aria-hidden", "true");
// Configure title text element
this.titleElement = this.titleBar.add(this.newLabel({}));
this.titleElement.element.setAttribute("name", "title");
this.titleElement.element.style.cursor = "default";
this.titleElement.element.style.flexGrow = "1";
this.titleElement.element.style.userSelect = "none";
this.element.setAttribute("aria-labelledby", this.titleElement.id);
// Configure title close element
this.titleCloseBox = this.titleBar.add(this.newPanel({}));
this.titleCloseBox.element.setAttribute("name", "title-close-box");
this.titleCloseBox.element.style.removeProperty("min-width");
this.titleClose = this.titleCloseBox.add(this.newButton({
focusable: false,
name : "{app.close}",
toolTip : "{app.close}"
// Title bar title
this.title = this.titleBar.add(this.newLabel({
localized: true,
text : options.title
}));
this.titleClose.element.setAttribute("name", "title-close");
this.title.element.setAttribute("name", "title");
this.title.setProperty("sim", "");
// Title bar close
this.titleClose = this.titleBar.add(this.newButton({
toolTip: "{app.close}"
}));
this.titleClose.element.setAttribute("name", "close");
this.titleClose.element.setAttribute("aria-hidden", "true");
this.titleClose.addClickListener(e=>this.onclose(e));
// Configure client area
this.client = this.body.add(this.newPanel({
overflowX: "hidden",
overflowY: "hidden"
}));
this.client.element.style.flexGrow = "1";
// Client area
this.client = this.body.add(this.newPanel());
this.client.element.setAttribute("name", "client");
this.client.element.addEventListener(
"pointerdown", e=>this.onclientdown(e));
// Configure properties
this.setTitle(this.title);
if (this.shown)
this.setClientSize(this.initialHeight, this.initialWidth);
application.addComponent(this);
this.setSize(options.width, options.height);
}
@@ -117,20 +90,19 @@ Toolkit.Window = class Window extends Toolkit.Panel {
this.closeListeners.push(listener);
}
// Specify the size of the client rectangle in pixels
setClientSize(width, height) {
let bounds = this.getBounds();
let client = this.client.getBounds();
this.setSize(
width + bounds.width - client.width,
height + bounds.height - client.height
);
// Retrieve the window's title text
getTitle() {
return this.title.getText();
}
// Specify the height of the component
setHeight(height, minimum) {
this.client && this.client.setHeight(height, minimum);
}
// Specify the window's title text
setTitle(title) {
this.title = title || "";
this.localize();
this.title.setText(title);
}
// Specify whether the component is visible
@@ -146,6 +118,12 @@ Toolkit.Window = class Window extends Toolkit.Panel {
this.firstShow();
}
// Specify the width of the component
setWidth(width, minimum) {
this.client && this.client.setWidth(
Math.max(64, width || 0), minimum);
}
///////////////////////////// Package Methods /////////////////////////////
@@ -155,13 +133,23 @@ Toolkit.Window = class Window extends Toolkit.Panel {
if (this.lastFocus != this)
this.lastFocus.focus();
else this.element.focus();
this.parent.bringToFront(this);
}
///////////////////////////// Private Methods /////////////////////////////
// Position the window in the center of the desktop
center() {
let bounds = this.getBounds();
let desktop = this.parent.getBounds();
this.contain(
Math.floor((desktop.width - bounds.width ) / 2),
Math.ceil ((desktop.height - bounds.height) / 2),
desktop, bounds
);
}
// Position the window using a tentative location in the desktop
contain(x, y, desktop, bounds, client) {
desktop = desktop || this.parent.getBounds();
@@ -213,28 +201,6 @@ Toolkit.Window = class Window extends Toolkit.Panel {
// The window is being displayed for the first time
firstShow() {
this.shown = true;
// Configure the initial size of the window
this.setClientSize(this.initialWidth, this.initialHeight);
// Configure the initial position of the window
if (!this.initialCenter)
return;
let bounds = this.getBounds();
let desktop = this.parent.getBounds();
this.contain(
Math.floor((desktop.width - bounds.width ) / 2),
Math.ceil ((desktop.height - bounds.height) / 2),
desktop, bounds
);
}
// Update display text with localized strings
localize() {
let title = this.title;
if (this.application)
title = this.application.translate(title, this);
this.titleElement.element.innerText = title;
}
// Focus lost event capture
@@ -243,13 +209,6 @@ Toolkit.Window = class Window extends Toolkit.Panel {
this.element.setAttribute("focus", "false");
}
// Client pointer down event handler
onclientdown(e) {
e.preventDefault();
e.stopPropagation();
this.focus();
}
// Window close
onclose(e) {
for (let listener of this.closeListeners)
@@ -258,52 +217,45 @@ Toolkit.Window = class Window extends Toolkit.Panel {
// Focus gained event capture
onfocus(e) {
// Configure element
this.element.setAttribute("focus", "true");
// Working variables
let target = e.target;
// Delegate focus to the most recently focused component
if (target == this.element)
target = this.lastFocus;
// The component is not visible: focus on self instead
if ("component" in target && !target.component.isVisible())
target = this.element;
// Configure instance fields
this.lastFocus = target;
// Transfer focus to the correct component
if (target != e.target)
target.focus();
this.parent.bringToFront(this);
}
// Key down event handler
// Key pressed event handler
onkeydown(e) {
// Processing by key
switch (e.key) {
default: return;
}
// Only listening for Escape while dragging
if (e.key != "Escape" || this.dragPointer === null)
return;
// Configure event
e.preventDefault();
e.stopPropagation();
// Restore the window's position
let desktop = this.parent.getBounds();
this.setLocation(
this.dragBounds.x - desktop.x,
this.dragBounds.y - desktop.y
);
// Restore the window's size
if (this.dragEdge != null)
this.setSize(this.dragClient.width, this.dragClient.height);
// Configure instance fields
this.element.releasePointerCapture(this.dragPointer);
this.dragPointer = null;
}
// Pointer down event handler
onpointerdown(e) {
// Configure event
e.preventDefault();
e.stopPropagation();
// Configure element
this.focus();
// Error checking
if (
e.button != 0 ||
@@ -312,12 +264,19 @@ Toolkit.Window = class Window extends Toolkit.Panel {
// Configure instance fields
this.dragBounds = this.getBounds();
this.dragEdge = this.edge(e, this.dragBounds);
this.dragClient = this.client.getBounds();
this.dragCursor.x = e.x;
this.dragCursor.y = e.y;
this.dragEdge = this.edge(e, this.dragBounds);
// Don't perform a move if the cursor isn't in the title bar
let title = this.titleBar.getBounds();
if (this.dragEdge == null && e.y >= title.y + title.height)
return;
// Configure element
this.element.setPointerCapture(e.pointerId);
this.dragPointer = e.pointerId;
}
// Pointer move event handler
@@ -337,12 +296,11 @@ Toolkit.Window = class Window extends Toolkit.Panel {
}
// Working variables
let rX = e.x - this.dragCursor.x;
let rY = e.y - this.dragCursor.y;
let bounds = this.getBounds();
let desktop = this.parent.getBounds();
let client = this.client.getBounds();
let minHeight = bounds.height - client.height;
let rX = e.x - this.dragCursor.x;
let rY = e.y - this.dragCursor.y;
let bounds = this.getBounds();
let desktop = this.parent.getBounds();
let client = this.client.getBounds();
// Move the window
if (this.dragEdge == null) {
@@ -358,7 +316,7 @@ Toolkit.Window = class Window extends Toolkit.Panel {
if (this.dragEdge.startsWith("n")) {
let maxTop = desktop.height - client.y + bounds.y;
let top = this.dragBounds.y - desktop.y + rY;
let height = this.dragBounds.height - rY;
let height = this.dragClient.height - rY;
// Restrict window bounds
if (top > maxTop) {
@@ -369,9 +327,9 @@ Toolkit.Window = class Window extends Toolkit.Panel {
height += top;
top = 0;
}
if (height < minHeight) {
top -= minHeight - height;
height = minHeight;
if (height < 0) {
top += height;
height = 0;
}
// Configure element
@@ -383,7 +341,7 @@ Toolkit.Window = class Window extends Toolkit.Panel {
if (this.dragEdge.endsWith("w")) {
let maxLeft = desktop.width - 16;
let left = this.dragBounds.x - desktop.x + rX;
let width = this.dragBounds.width - rX;
let width = this.dragClient.width - rX;
// Restrict window bounds
if (left > maxLeft) {
@@ -402,11 +360,11 @@ Toolkit.Window = class Window extends Toolkit.Panel {
// Resizing on the east edge
if (this.dragEdge.endsWith("e")) {
let width = this.dragBounds.width + rX;
let width = this.dragClient.width + rX;
// Restrict window bounds
width = Math.max(64, width);
width = Math.max(width, -this.dragBounds.x + 16);
width = Math.max(width, -this.dragClient.x + 16);
// Configure element
this.setWidth(width);
@@ -414,10 +372,10 @@ Toolkit.Window = class Window extends Toolkit.Panel {
// Resizing on the south edge
if (this.dragEdge.startsWith("s")) {
let height = this.dragBounds.height + rY;
let height = this.dragClient.height + rY;
// Restrict window bounds
height = Math.max(minHeight, height);
height = Math.max(0, height);
// Configure element
this.setHeight(height);
@@ -438,6 +396,7 @@ Toolkit.Window = class Window extends Toolkit.Panel {
// Configure element
this.element.releasePointerCapture(e.pointerId);
this.dragPointer = null;
}
};