diff options
author | Kyle Gunger <kgunger12@gmail.com> | 2024-11-15 02:57:31 -0500 |
---|---|---|
committer | Kyle Gunger <kgunger12@gmail.com> | 2024-11-15 02:57:31 -0500 |
commit | ffae2dfebf7a79415a9fec63ecbfea45f53ad429 (patch) | |
tree | 70e053a103d4f8d882d8f22ca0dac571dc793ba2 | |
parent | d00bfda4ffafa8c2119c97217a259d3ef97d0af7 (diff) |
Better touch support
-rw-r--r-- | scripts/gui-common/widgets.js | 245 | ||||
-rw-r--r-- | styles/widgets.css | 36 |
2 files changed, 228 insertions, 53 deletions
diff --git a/scripts/gui-common/widgets.js b/scripts/gui-common/widgets.js index 0591da6..78402c5 100644 --- a/scripts/gui-common/widgets.js +++ b/scripts/gui-common/widgets.js @@ -1,3 +1,16 @@ +( function () { + window.SCROLLING = false; + function scroll(event) { + window.SCROLLING = true; + } + function unscroll(event) { + window.SCROLLING = false; + } + window.addEventListener("scroll", scroll); + window.addEventListener("scrollend", unscroll); +})(); + + /** * The base Widget class. Represents an interactible * value-producing object in the browser, like an input. @@ -81,10 +94,19 @@ class Widget extends EventTarget{ /** @param {TouchEvent} event */ #emitTouchEvent(event) { + //if (window.SCROLLING) + // return; + + if (event.type == "touchstart") + this.element.classList.add("touch"); + else if (event.type == "touchend") + this.element.classList.remove("touch"); + if (this.inactive) this.dispatchEvent(new CustomEvent("inactive", {detail: {widget: this, event: new TouchEvent(event.type, event)}})); else this.dispatchEvent(new TouchEvent(event.type, event)); + event.preventDefault(); } @@ -110,6 +132,8 @@ class WidgetButton extends Widget /** @type {number} */ pressing = 0; + #touching = {count: 0}; + gone = 0; #bound = null; @@ -122,6 +146,8 @@ class WidgetButton extends Widget this.addEventListener("mouseup", this.#unpress); this.addEventListener("mouseleave", this.#leave); this.addEventListener("mouseenter", this.#enter); + this.addEventListener("touchstart", this.#touchstart); + this.addEventListener("touchend", this.#touchend); this.#bound = this.#unpress.bind(this); } @@ -166,12 +192,45 @@ class WidgetButton extends Widget this.gone = 0; window.removeEventListener("mouseup", this.#bound); } + + /** + * @param {TouchEvent} event + */ + #touchend(event) + { + for(let i of event.changedTouches) + { + if (this.#touching[i.identifier] == 1) + { + delete this.#touching[i.identifier]; + this.#touching.count--; + } + } + + if (this.#touching.count == 0) + this.set(false); + } + + /** + * @param {TouchEvent} event + */ + #touchstart(event) + { + if (this.#touching.count == 0) + this.set(true); + + for(let i of event.changedTouches) + { + this.#touching.count++; + this.#touching[i.identifier] = 1; + } + } } /** * A toggle widget, similar to a WidgetCheckbox, but meant * to be used for on/off power states. - * @extends Widget<boolean> + * @extends Widget<*> */ class WidgetToggle extends Widget { @@ -196,7 +255,8 @@ class WidgetToggle extends Widget this.addEventListener("mouseup", this.#toggle); this.addEventListener("mouseleave", this.#leave); this.addEventListener("mouseenter", this.#enter); - this.addEventListener("change", this.#update_ui); + this.addEventListener("change", this.#update_ui); + this.addEventListener("touchend", this.#touchend); this.#bound = this.#toggle.bind(this); } @@ -250,6 +310,30 @@ class WidgetToggle extends Widget window.removeEventListener("mouseup", this.#bound); } + /** @param {TouchEvent} event */ + #touchend(event) + { + if (event.changedTouches.length < 1) + return; + + let rect = this.element.getBoundingClientRect(); + + for (let i of event.changedTouches) + { + if (i.clientX < rect.right && + i.clientX > rect.left && + i.clientY > rect.top && + i.clientY < rect.bottom + ) { + if (this.get() == this.#trueVal) + this.set(this.#falseVal); + else + this.set(this.#trueVal); + return; + } + } + } + setFalseVal(fv) { if (this.get() == this.#falseVal) @@ -302,16 +386,16 @@ class WidgetDragable extends Widget /** @type {(e: MouseEvent, b: number) => void} */ #m_move = null; - /** @type {(e: TouchEvent) => void} */ - #t_up = null; - /** @type {(e: TouchEvent) => void} */ - #t_move = null; + #touches = {count: 0}; + + /** @type {number} */ + #max_t; /** * Constructor */ - constructor () + constructor (maxTouches = 1) { super(); @@ -328,12 +412,13 @@ class WidgetDragable extends Widget this.addEventListener("touchend", this.#touch); this.addEventListener("touchmove", this.#touch); this.addEventListener("touchcancel", this.#touch); + + this.#max_t = 2; } /** @param {MouseEvent} event */ #press(event) { - console.log("Press!"); this.#primed |= (1 << event.button); this.#move(event); } @@ -341,7 +426,6 @@ class WidgetDragable extends Widget /** @param {MouseEvent} event */ #unpress(event) { - console.log("Unpress!"); if (((1 << event.button) & this.#primed) == 0) return; this.#primed -= (1 << event.button); @@ -384,12 +468,47 @@ class WidgetDragable extends Widget } /** + * Set the maximum number of continuous touches to allow + * @param {number} t + */ + setMaxTouches(t) + { + this.#max_t = t; + } + + /** * @param {TouchEvent} event */ #touch(event) { - console.log(event.type, event.changedTouches); - event.preventDefault(); + if (event.type == "touchstart") + { + if (this.#touches.count < this.#max_t) + { + this.#touches[event.changedTouches[0].identifier] = 1; + this.#touches.count++; + } + else + return; + } + + if (event.type == "touchend") + { + if (this.#touches[event.changedTouches[0].identifier] == 1) + { + delete this.#touches[event.changedTouches[0].identifier]; + this.#touches.count--; + } + } + + let out = []; + for(let t of event.changedTouches) + { + if (this.#touches[t.identifier] == 1) + out.push(t); + } + + this.touch(event.type, out); } } @@ -450,33 +569,21 @@ class WidgetSlider extends WidgetDragable this.#u_detail = this.update_detail.bind(this); } - /** - * @param {MouseEvent} event - * @param {number} btns - * @param {boolean} gone - */ - move(event, btns, gone) + #common_move(x, y) { - if (btns == 0) - { - if (event.type == "mouseup") - this.set(this.#tmpNum); - return; - } - let rect = this.element.getBoundingClientRect(); let top = 0, bot = 0, point = 0; if (this.element.classList.contains("h")) { top = rect.right; bot = rect.left; - point = event.clientX; + point = x; } else { top = rect.bottom; bot = rect.top; - point = top - event.clientY + bot; + point = top - y + bot; } if (point < bot && this.#tmpNum != this.#min) @@ -495,6 +602,40 @@ class WidgetSlider extends WidgetDragable this.#update_ui(); } + /** + * @param {MouseEvent} event + * @param {number} btns + * @param {boolean} gone + */ + move(event, btns, gone) + { + if (btns == 0) + { + if (event.type == "mouseup") + this.set(this.#tmpNum); + return; + } + + this.#common_move(event.clientX, event.clientY); + } + + /** + * @param {"touchstart" | "touchend" | "touchmove"} type + * @param {Touch[]} touches + */ + touch(type, touches) + { + if (type == "touchend") + { + this.set(this.#tmpNum); + } + + if (touches.length < 1) + return; + + this.#common_move(touches[0].clientX, touches[0].clientY); + } + #change () { this.#tmpNum = this.get(); @@ -652,24 +793,13 @@ class WidgetColorWheel extends WidgetDragable this.addEventListener("change", this.#change); } - /** - * @param {MouseEvent} event - * @param {number} btns - */ - move(event, btns) + #common_move(x, y) { - if (btns == 0) - { - if (event.type == "mouseup") - this.set(this.#tmpColor); - return; - } - let rect = this.element.getBoundingClientRect(); // Points - let tmpX = event.clientX - rect.width / 2; - let tmpY = rect.bottom - event.clientY + rect.top - rect.height / 2; + let tmpX = x - rect.width / 2; + let tmpY = rect.bottom - y + rect.top - rect.height / 2; // Percents tmpX = (tmpX - rect.left) / ((rect.right - rect.left) / 2); @@ -689,6 +819,41 @@ class WidgetColorWheel extends WidgetDragable this.update_detail(tmpX, tmpY, mag); } + /** + * @param {MouseEvent} event + * @param {number} btns + */ + move(event, btns) + { + if (btns == 0) + { + if (event.type == "mouseup") + this.set(this.#tmpColor); + return; + } + + this.#common_move(event.clientX, event.clientY); + } + + /** + * @param {"touchstart" | "touchend" | "touchmove"} type + * @param {Touch[]} touches + */ + touch(type, touches) + { + if (type == "touchend") + { + this.set(this.#tmpColor); + } + + if (touches.length < 1) + { + return; + } + + this.#common_move(touches[0].clientX, touches[0].clientY); + } + #change () { this.#tmpColor = this.get(); diff --git a/styles/widgets.css b/styles/widgets.css index feeeb73..987c857 100644 --- a/styles/widgets.css +++ b/styles/widgets.css @@ -34,7 +34,7 @@ input { background-color: var(--w-bg-hover); } -.widget:active { +.widget:active, .widget.touch { background-color: var(--w-bg-active); } @@ -65,7 +65,7 @@ input { background-color: var(--w-bg); } -.widget.inactive:active, .widget:disabled:active { +.widget.inactive:active, .widget:disabled:active, .widget.inactive.touch, .widget.touch:disabled { background-color: var(--w-bg); } @@ -81,13 +81,13 @@ input { -webkit-user-select: none; } -.button:active { +.button:active, .button.touch { transform:translate(5px, 5px); box-shadow: none !important; border:none; } -.button.inactive:active, .button:disabled:active { +.button.inactive:active, .button.inactive.touch, .button:disabled:active, .button.touch:disabled { transform: none; box-shadow: 1px 1px var(--w-shadow-inactive), 3px 3px var(--w-shadow-inactive), 5px 5px var(--w-shadow-inactive) !important; border: inherit; @@ -160,7 +160,7 @@ input { background-color: var(--w-sl-fill-hover); } -.slider:active > .fill::before { +.slider:active > .fill::before, .slider.touch > .fill::before { background-color: var(--w-sl-fill-active); transition-duration: 0s; } @@ -218,13 +218,18 @@ input { left: calc(-1.2 * var(--base-unit) / 2 + 100% * var(--percent)); } -.slider:active > .detail { +.slider:active > .detail, .slider.touch > .detail { background-color: rgba(0, 132, 255, 1); color: rgba(255, 255, 255, 1); transition-duration: 0s; } -.color-light:active > .detail, .color-temp:active > .detail, .color-wheel:active > .detail { +.color-light:active > .detail, +.color-temp:active > .detail, +.color-wheel:active > .detail, +.color-light.touch > .detail, +.color-temp.touch > .detail, +.color-wheel.touch > .detail { background-color: var(--detail); transition-duration: 0s; } @@ -256,7 +261,7 @@ input { border-color: var(--w-cb-inactive-outline-hover); } -.checkbox:active { +.checkbox:active, .checkbox.touch { border: 7px solid var(--w-cb-inactive-outline-active); } @@ -275,7 +280,7 @@ input { border-color: var(--w-cb-inactive-outline); } -.checkbox.inactive:active, .checkbox:disabled:active { +.checkbox.inactive:active, .checkbox:disabled:active, .checkbox.inactive.touch, .checkbox.touch:disabled { border: 7px solid var(--w-cb-inactive-outline); box-shadow: 5px 5px inset var(--w-shadow-inactive), 1px 1px var(--w-shadow-inactive), 3px 3px var(--w-shadow-inactive), 5px 5px var(--w-shadow-inactive) !important; } @@ -323,7 +328,12 @@ input { box-shadow: 1px 1px var(--w-shadow-inactive), 3px 3px var(--w-shadow-inactive), 5px 5px var(--w-shadow-inactive); } -.checkbox.inactive:checked:active, .checkbox.active.inactive:active, .checkbox:disabled:checked:active { +.checkbox.inactive:checked:active, +.checkbox.active.inactive:active, +.checkbox:disabled:checked:active, +.checkbox.inactive.touch:checked, +.checkbox.active.inactive.touch, +.checkbox:disabled.touch:checked { border: none; box-shadow: 1px 1px var(--w-shadow-inactive), 3px 3px var(--w-shadow-inactive), 5px 5px var(--w-shadow-inactive) !important; } @@ -362,7 +372,7 @@ input { box-sizing: border-box; } -.color-wheel:active::after { +.color-wheel:active::after, .color-wheel.touch::after { transition-duration: 0s; } @@ -387,7 +397,7 @@ input { left: calc(100% + 7px); } -.color-wheel:active > .detail { +.color-wheel:active > .detail, .color-wheel.touch > .detail { box-shadow: 1px 1px var(--w-shadow), 3px 3px var(--w-shadow), 5px 5px var(--w-shadow); } @@ -423,7 +433,7 @@ input { transition-duration: 0.15s; } -.color-temp:active::after, .color-light:active::after { +.color-temp:active::after, .color-light:active::after, .color-temp.touch::after, .color-light.touch::after { transition-duration: 0s; } |