virtual cursor

This commit is contained in:
booploops 2022-03-02 00:33:27 -08:00
parent e308160bec
commit 0f8b739705
6 changed files with 263 additions and 35 deletions

View file

@ -233,7 +233,8 @@ const app = new Vue({
topChromeVisible: true, topChromeVisible: true,
progresshover: false, progresshover: false,
windowControlPosition: "right", windowControlPosition: "right",
contentAreaScrolling: true contentAreaScrolling: true,
showCursor: false
}, },
collectionList: { collectionList: {
response: {}, response: {},
@ -303,7 +304,19 @@ const app = new Vue({
}, },
methods: { methods: {
simulateController() { simulateController() {
this.chrome.showCursor = true
let cursorPos = [0, 0];
let intTabIndex = 0 let intTabIndex = 0
let self = this
let cursorSpeed = 4
let scrollSpeed = 8
let buttonPressDelay = 500
let stickDeadZone = 0.2
let scrollGroup = null
let lastButtonPress = {
}
var sounds = { var sounds = {
Confirm: new Audio("./sounds/confirm.ogg"), Confirm: new Audio("./sounds/confirm.ogg"),
@ -311,6 +324,166 @@ const app = new Vue({
Hover: new Audio("./sounds/hover.ogg") Hover: new Audio("./sounds/hover.ogg")
} }
let element = document.elementFromPoint(0, 0)
let elementType = 0
function appLoop() {
var gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []);
if (!gamepads) {
return;
}
var gp = gamepads[0];
// LEFT STICK
if (gp.axes[0] > stickDeadZone) {
cursorPos[0] += (gp.axes[0] * cursorSpeed)
} else if (gp.axes[0] < -stickDeadZone) {
cursorPos[0] += (gp.axes[0] * cursorSpeed)
}
if (gp.axes[1] > stickDeadZone) {
cursorPos[1] += (gp.axes[1] * cursorSpeed)
} else if (gp.axes[1] < -stickDeadZone) {
cursorPos[1] += (gp.axes[1] * cursorSpeed)
}
// RIGHT STICK.
if (gp.axes[3] > stickDeadZone) {
$("#app-content").scrollTop($("#app-content").scrollTop() + (gp.axes[3] * scrollSpeed))
} else if (gp.axes[3] < -stickDeadZone) {
$("#app-content").scrollTop($("#app-content").scrollTop() + (gp.axes[3] * scrollSpeed))
}
if(scrollGroup) {
if (gp.axes[2] > stickDeadZone) {
console.log('axis 2 up')
$(scrollGroup).scrollLeft($(scrollGroup).scrollLeft() + (gp.axes[2] * scrollSpeed))
} else if (gp.axes[2] < -stickDeadZone) {
console.log('axis 2 dn')
$(scrollGroup).scrollLeft($(scrollGroup).scrollLeft() + (gp.axes[2] * scrollSpeed))
}
}
$(".cursor").css({
top: cursorPos[1] + "px",
left: cursorPos[0] + "px"
})
// A BUTTON
if (gp.buttons[0].pressed) {
if (!lastButtonPress["A"]) {
lastButtonPress["A"] = 0
}
if (Date.now() - lastButtonPress["A"] > buttonPressDelay) {
lastButtonPress["A"] = Date.now()
sounds.Confirm.play()
if (elementType == 0) {
document.activeElement.dispatchEvent(new Event("click"))
document.activeElement.dispatchEvent(new Event("controller-click"))
} else {
element.dispatchEvent(new Event("click"))
element.dispatchEvent(new Event("controller-click"))
}
}
}
// B BUTTON
if (gp.buttons[1].pressed) {
if (!lastButtonPress["B"]) {
lastButtonPress["B"] = 0
}
if (Date.now() - lastButtonPress["B"] > buttonPressDelay) {
lastButtonPress["B"] = Date.now()
if (elementType == 0) {
document.activeElement.dispatchEvent(new Event("contextmenu"))
setTimeout(()=>{
if($(".menu-option").length > 0) {
let bounds = $(".menu-option")[0].getBoundingClientRect()
cursorPos[0] = bounds.left + (bounds.width / 2)
cursorPos[1] = bounds.top + (bounds.height / 2)
}
}, 100)
} else {
element.dispatchEvent(new Event("contextmenu"))
}
}
}
// right bumper
if (gp.buttons[5].pressed) {
if (!lastButtonPress["RB"]) {
lastButtonPress["RB"] = 0
}
if (Date.now() - lastButtonPress["RB"] > buttonPressDelay) {
lastButtonPress["RB"] = Date.now()
app.navigateForward()
}
}
// left bumper
if (gp.buttons[4].pressed) {
if (!lastButtonPress["LB"]) {
lastButtonPress["LB"] = 0
}
if (Date.now() - lastButtonPress["LB"] > buttonPressDelay) {
lastButtonPress["LB"] = Date.now()
app.navigateBack()
}
}
// cursor hover
element = document.elementFromPoint(cursorPos[0], cursorPos[1])
if (element) {
let closest = element.closest("[tabindex], input, button, a")
let scrollGroupClo = element.closest(".v-hl-container")
if(scrollGroupClo) {
if(scrollGroupClo.classList.contains("v-hl-container")) {
scrollGroup = scrollGroupClo
scrollGroup.style["scroll-snap-type"] = "unset"
} else {
scrollGroup.style["scroll-snap-type"] = ""
scrollGroup = null
}
}
if (closest) {
elementType = 0
closest.focus()
} else {
if(closest) {
closest.blur()
}
elementType = 1
element.focus()
}
}
// console.log(gp.axes[0], gp.axes[1])
start = requestAnimationFrame(appLoop);
}
// controller pairing
notyf.error("Press the button on your controller to pair it to Cider.")
window.addEventListener("gamepadconnected", function (e) {
console.log("Gamepad connected at index %d: %s. %d buttons, %d axes.",
e.gamepad.index, e.gamepad.id,
e.gamepad.buttons.length, e.gamepad.axes.length);
notyf.success("Pairing successful!")
appLoop()
}, { once: true });
document.addEventListener("keydown", (e) => { document.addEventListener("keydown", (e) => {
sounds.Confirm.currentTime = 0 sounds.Confirm.currentTime = 0
sounds.Menu.currentTime = 0 sounds.Menu.currentTime = 0
@ -319,51 +492,92 @@ const app = new Vue({
console.log(e.key) console.log(e.key)
switch (e.key) { switch (e.key) {
default: default:
break; break;
case "ArrowLeft": case "ArrowLeft":
e.preventDefault() e.preventDefault()
cursorPos[0] -= cursorSpeed
break; break;
case "ArrowRight": case "ArrowRight":
e.preventDefault() e.preventDefault()
cursorPos[0] += cursorSpeed
break; break;
case "ArrowUp": case "ArrowUp":
sounds.Hover.play()
if(intTabIndex <= 0) {
intTabIndex = 0
}else{
intTabIndex--
}
$(tabbable[intTabIndex]).focus()
// $("#app-content").scrollTop($(document.activeElement).offset().top)
e.preventDefault() e.preventDefault()
cursorPos[1] -= cursorSpeed
// sounds.Hover.play()
// if(intTabIndex <= 0) {
// intTabIndex = 0
// }else{
// intTabIndex--
// }
// $(tabbable[intTabIndex]).focus()
// $("#app-content").scrollTop($(document.activeElement).offset().top)
break; break;
case "ArrowDown": case "ArrowDown":
sounds.Hover.play()
if(intTabIndex < tabbable.length) {
intTabIndex++
}else{
intTabIndex = tabbable.length
}
$(tabbable[intTabIndex]).focus()
// $("#app-content").scrollTop($(document.activeElement).offset().top)
e.preventDefault() e.preventDefault()
cursorPos[1] += cursorSpeed
// if(intTabIndex < tabbable.length) {
// intTabIndex++
// }else{
// intTabIndex = tabbable.length
// }
// $(tabbable[intTabIndex]).focus()
// $("#app-content").scrollTop($(document.activeElement).offset().top)
break; break;
case "c": case "c":
app.resetState() app.resetState()
break; break;
case "x": case "x":
sounds.Menu.play() // set cursorPos to the top right of the screen
document.activeElement.dispatchEvent(new Event("contextmenu")) // sounds.Menu.play()
if (elementType == 0) {
document.activeElement.dispatchEvent(new Event("contextmenu"))
} else {
element.dispatchEvent(new Event("contextmenu"))
}
e.preventDefault() e.preventDefault()
break; break;
case "z": case "z":
sounds.Confirm.play() sounds.Confirm.play()
document.activeElement.dispatchEvent(new Event("click")) if (elementType == 0) {
document.activeElement.dispatchEvent(new Event("controller-click")) document.activeElement.dispatchEvent(new Event("click"))
document.activeElement.dispatchEvent(new Event("controller-click"))
} else {
element.dispatchEvent(new Event("click"))
element.dispatchEvent(new Event("controller-click"))
}
e.preventDefault() e.preventDefault()
break; break;
} }
$(".cursor").css({
top: cursorPos[1] + "px",
left: cursorPos[0] + "px"
})
function lerp(a, b, n) {
return (1 - n) * a + n * b
}
element = document.elementFromPoint(cursorPos[0], cursorPos[1])
if (element) {
let closest = element.closest("[tabindex], input, button, a")
if (closest) {
elementType = 0
closest.focus()
} else {
elementType = 1
element.focus()
}
}
console.log(element)
}); });
}, },
songLinkShare(amUrl) { songLinkShare(amUrl) {
@ -978,7 +1192,7 @@ const app = new Vue({
}, 500) }, 500)
ipcRenderer.invoke("renderer-ready", true) ipcRenderer.invoke("renderer-ready", true)
document.querySelector("#LOADER").remove() document.querySelector("#LOADER").remove()
if(this.cfg.general.themeUpdateNotification) { if (this.cfg.general.themeUpdateNotification) {
this.checkForThemeUpdates() this.checkForThemeUpdates()
} }
}, },
@ -992,7 +1206,7 @@ const app = new Vue({
.then(res => { .then(res => {
if (res[0].sha != theme.commit) { if (res[0].sha != theme.commit) {
const notify = notyf.open({ className: "notyf-info", type: "info", message: `[Themes] ${theme.name} has an update available.` }) const notify = notyf.open({ className: "notyf-info", type: "info", message: `[Themes] ${theme.name} has an update available.` })
notify.on("click", ()=>{ notify.on("click", () => {
app.appRoute("themes-github") app.appRoute("themes-github")
notyf.dismiss(notify) notyf.dismiss(notify)
}) })
@ -1038,9 +1252,9 @@ const app = new Vue({
} }
if (directives[directive]) { if (directives[directive]) {
return this.chrome.appliedTheme.info.directives[directive].value return this.chrome.appliedTheme.info.directives[directive].value
} else if(this.cfg.visual.directives[directive]) { } else if (this.cfg.visual.directives[directive]) {
return this.cfg.visual.directives.windowLayout return this.cfg.visual.directives.windowLayout
}else{ } else {
return "" return ""
} }
}, },
@ -3470,7 +3684,7 @@ const app = new Vue({
let artworkSize = 50 let artworkSize = 50
if (app.getThemeDirective("lcdArtworkSize") != "") { if (app.getThemeDirective("lcdArtworkSize") != "") {
artworkSize = app.getThemeDirective("lcdArtworkSize") artworkSize = app.getThemeDirective("lcdArtworkSize")
}else if(this.cfg.visual.directives.windowLayout == "twopanel") { } else if (this.cfg.visual.directives.windowLayout == "twopanel") {
artworkSize = 70 artworkSize = 70
} }
this.currentArtUrl = ''; this.currentArtUrl = '';

View file

@ -3152,6 +3152,19 @@ body[platform='darwin'] {
flex-direction: column; flex-direction: column;
} }
.cursor {
background: rgb(255 255 255 / 50%);
width: 16px;
height: 16px;
position: fixed;
z-index: 9999999999;
pointer-events: none;
border-radius: 100%;
box-shadow: 0px 0px 0px 2px rgb(200 200 200 / 100%);
top: 0;
left: 0;
}
@import url("less/macos.less"); @import url("less/macos.less");
@import url("less/linux.less"); @import url("less/linux.less");
@import url("less/compact.less"); @import url("less/compact.less");

View file

@ -1,5 +1,5 @@
<script type="text/x-template" id="artist-chip"> <script type="text/x-template" id="artist-chip">
<div class="artist-chip" @click.self="route"> <div class="artist-chip" @click.self="route" tabindex="0">
<div class="artist-chip__image"> <div class="artist-chip__image">
<mediaitem-artwork v-if="artist.id != null" :url="artist.attributes.artwork.url" :size="32"></mediaitem-artwork> <mediaitem-artwork v-if="artist.id != null" :url="artist.attributes.artwork.url" :size="32"></mediaitem-artwork>
</div> </div>

View file

@ -13,7 +13,7 @@
@mouseover="showInLibrary = true" @mouseover="showInLibrary = true"
@mouseleave="showInLibrary = false" @mouseleave="showInLibrary = false"
@dblclick="route()" @dblclick="route()"
@controller-click.self="route()" @controller-click="route()"
tabindex="0" tabindex="0"
:class="[{'mediaitem-selected': app.select_hasMediaItem(guid)}, addClasses]"> :class="[{'mediaitem-selected': app.select_hasMediaItem(guid)}, addClasses]">
<template v-if="isVisible"> <template v-if="isVisible">

View file

@ -2,7 +2,7 @@
<div <div
tabindex="0" tabindex="0"
@click.self="app.routeView(item)" @click.self="app.routeView(item)"
@controller-click.self="app.routeView(item)" @controller-click="app.routeView(item)"
@contextmenu.self="contextMenu" @contextmenu.self="contextMenu"
class="cd-mediaitem-square" :class="getClasses()" @contextmenu="contextMenu" class="cd-mediaitem-square" :class="getClasses()" @contextmenu="contextMenu"
v-observe-visibility="{callback: visibilityChanged}" v-observe-visibility="{callback: visibilityChanged}"

View file

@ -80,6 +80,7 @@
</div> </div>
</transition> </transition>
<%- include('app/panels'); %> <%- include('app/panels'); %>
<div class="cursor" v-if="chrome.showCursor"></div>
</div> </div>
<% for(var i=0; i < Object.keys(env.components).length ; i++) {%> <% for(var i=0; i < Object.keys(env.components).length ; i++) {%>