summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKyle Gunger <kgunger12@gmail.com>2024-11-15 02:57:31 -0500
committerKyle Gunger <kgunger12@gmail.com>2024-11-15 02:57:31 -0500
commitffae2dfebf7a79415a9fec63ecbfea45f53ad429 (patch)
tree70e053a103d4f8d882d8f22ca0dac571dc793ba2
parentd00bfda4ffafa8c2119c97217a259d3ef97d0af7 (diff)
Better touch support
-rw-r--r--scripts/gui-common/widgets.js245
-rw-r--r--styles/widgets.css36
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;
}