mirror of
https://github.com/CharaChorder/DeviceManager.git
synced 2026-04-19 12:48:55 +00:00
feat: device flip
This commit is contained in:
@@ -14,7 +14,6 @@
|
|||||||
CompiledLayout,
|
CompiledLayout,
|
||||||
CompiledLayoutKey,
|
CompiledLayoutKey,
|
||||||
} from "$lib/assets/layouts/layout.d.ts";
|
} from "$lib/assets/layouts/layout.d.ts";
|
||||||
import { setting } from "$lib/setting.js";
|
|
||||||
|
|
||||||
const { scale, margin, strokeWidth, fontSize, iconFontSize } =
|
const { scale, margin, strokeWidth, fontSize, iconFontSize } =
|
||||||
getContext<VisualLayoutConfig>("visual-layout-config");
|
getContext<VisualLayoutConfig>("visual-layout-config");
|
||||||
@@ -120,9 +119,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function edit(index: number) {
|
function edit(index: number) {
|
||||||
const keyInfo = layoutInfo.keys[index];
|
const keyInfo =
|
||||||
|
layoutInfo.keys.find(({ id }) => id === index) ??
|
||||||
|
layoutInfo.fixedKeys.find(({ id }) => id === index);
|
||||||
if (!keyInfo) return;
|
if (!keyInfo) return;
|
||||||
const clickedGroup = groupParent.children.item(index) as SVGGElement;
|
const clickedGroup = groupParent.querySelector(
|
||||||
|
`g[data-id="${index}"]`,
|
||||||
|
) as SVGGElement;
|
||||||
const nextAction =
|
const nextAction =
|
||||||
get(layout)[get(activeProfile)]![get(activeLayer)]?.[keyInfo.id];
|
get(layout)[get(activeProfile)]![get(activeLayer)]?.[keyInfo.id];
|
||||||
const currentAction =
|
const currentAction =
|
||||||
@@ -190,7 +193,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
let focusKey: CompiledLayoutKey;
|
let focusKey: CompiledLayoutKey;
|
||||||
let groupParent: SVGElement;
|
let groupParent: SVGGElement;
|
||||||
|
let rotationTarget: SVGCircleElement;
|
||||||
let rotationSetting = $derived(
|
let rotationSetting = $derived(
|
||||||
$deviceMeta?.settings
|
$deviceMeta?.settings
|
||||||
.find((it) => it.name === "misc")
|
.find((it) => it.name === "misc")
|
||||||
@@ -201,26 +205,43 @@
|
|||||||
? ($settings[$activeProfile]?.[rotationSetting.id]?.value ?? 90)
|
? ($settings[$activeProfile]?.[rotationSetting.id]?.value ?? 90)
|
||||||
: 90,
|
: 90,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let flippedSetting = $derived(
|
||||||
|
$deviceMeta?.settings
|
||||||
|
.find((it) => it.name === "misc")
|
||||||
|
?.items.find((it) => it.name === "device orientation"),
|
||||||
|
);
|
||||||
|
let flipped = $derived(
|
||||||
|
flippedSetting
|
||||||
|
? $settings[$activeProfile]?.[flippedSetting.id]?.value !== 0
|
||||||
|
: false,
|
||||||
|
);
|
||||||
|
|
||||||
let draggingRotation = $state(90);
|
let draggingRotation = $state(90);
|
||||||
let isDragging = $state(false);
|
let isDragging = $state(false);
|
||||||
let rotation = $derived(isDragging ? draggingRotation : settingRotation);
|
let rotation = $derived(isDragging ? draggingRotation : settingRotation);
|
||||||
let dragOffset = 0;
|
let dragOffset = 0;
|
||||||
|
|
||||||
function calcDragOffset(event: MouseEvent) {
|
function calcDragOffset(event: MouseEvent) {
|
||||||
const offset = groupParent.getBoundingClientRect();
|
const offset = rotationTarget.getBoundingClientRect();
|
||||||
const centerX =
|
const cx = offset.x + offset.width / 2;
|
||||||
offset.x +
|
const cy = offset.y + offset.height / 2;
|
||||||
(layoutInfo.rotationAnchor?.[0] ?? 0) *
|
const a = Math.atan2(event.x - cx, event.y - cy) * (180 / Math.PI) + 90;
|
||||||
scale *
|
return flipped ? (a + 180) % 360 : a;
|
||||||
(offset.width / (layoutInfo.size[0] * scale));
|
}
|
||||||
const centerY =
|
|
||||||
offset.y +
|
function toggleFlip() {
|
||||||
(layoutInfo.rotationAnchor?.[1] ?? 0) *
|
changes.update((changes) => {
|
||||||
scale *
|
changes.push([
|
||||||
(offset.height / (layoutInfo.size[1] * scale));
|
{
|
||||||
return (
|
type: ChangeType.Setting,
|
||||||
Math.atan2(event.x - centerX, event.y - centerY) * (180 / Math.PI) + 90
|
id: flippedSetting!.id,
|
||||||
);
|
setting: flipped ? 0 : 1,
|
||||||
|
profile: get(activeProfile),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
return changes;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function dragRotation(event: MouseEvent) {
|
function dragRotation(event: MouseEvent) {
|
||||||
@@ -261,27 +282,33 @@
|
|||||||
|
|
||||||
<svg
|
<svg
|
||||||
class="print"
|
class="print"
|
||||||
viewBox="0 0 {layoutInfo.size[0] * scale} {layoutInfo.size[1] * scale}"
|
viewBox="0 {flipped ? margin * -3 : 0} {layoutInfo.size[0] *
|
||||||
bind:this={groupParent}
|
scale} {layoutInfo.size[1] * scale}"
|
||||||
transition:fly={{ y: 48, easing: expoOut }}
|
transition:fly={{ y: 48, easing: expoOut }}
|
||||||
onmousemove={dragRotation}
|
onmousemove={dragRotation}
|
||||||
onmouseup={dragDisable}
|
onmouseup={dragDisable}
|
||||||
>
|
>
|
||||||
|
<g
|
||||||
|
bind:this={groupParent}
|
||||||
|
transform="rotate({flipped ? 180 : 0})"
|
||||||
|
transform-origin="{(layoutInfo.rotationAnchor?.[0] ?? 0) *
|
||||||
|
scale} {(layoutInfo.size[1] * scale) / 2}"
|
||||||
|
>
|
||||||
<g
|
<g
|
||||||
transform-origin="{(layoutInfo.rotationAnchor?.[0] ?? 0) *
|
transform-origin="{(layoutInfo.rotationAnchor?.[0] ?? 0) *
|
||||||
scale} {(layoutInfo.rotationAnchor?.[1] ?? 0) * scale}"
|
scale} {(layoutInfo.rotationAnchor?.[1] ?? 0) * scale}"
|
||||||
transform="rotate({-(rotation - 90)})"
|
transform="rotate({-(rotation - 90)})"
|
||||||
class="group"
|
class="group"
|
||||||
>
|
>
|
||||||
{#each layoutInfo.keys as key, i}
|
{#each layoutInfo.keys as key}
|
||||||
<KeyboardKey
|
<KeyboardKey
|
||||||
{i}
|
|
||||||
{key}
|
{key}
|
||||||
|
{flipped}
|
||||||
onfocusin={() => (focusKey = key)}
|
onfocusin={() => (focusKey = key)}
|
||||||
onclick={() => edit(i)}
|
onclick={() => edit(key.id)}
|
||||||
onkeypress={({ key }) => {
|
onkeypress={(event) => {
|
||||||
if (key === "Enter") {
|
if (event.key === "Enter") {
|
||||||
edit(i);
|
edit(key.id);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@@ -291,6 +318,7 @@
|
|||||||
role="button"
|
role="button"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
onmousedown={dragEnable}
|
onmousedown={dragEnable}
|
||||||
|
ondblclick={toggleFlip}
|
||||||
class="handle"
|
class="handle"
|
||||||
x={(layoutInfo.size[0] * scale) / 2 - (0.5 * scale) / 2}
|
x={(layoutInfo.size[0] * scale) / 2 - (0.5 * scale) / 2}
|
||||||
y={layoutInfo.size[1] * scale + margin - 0.05 * scale}
|
y={layoutInfo.size[1] * scale + margin - 0.05 * scale}
|
||||||
@@ -302,32 +330,48 @@
|
|||||||
stroke-width={strokeWidth}
|
stroke-width={strokeWidth}
|
||||||
/>
|
/>
|
||||||
{#if isDragging}
|
{#if isDragging}
|
||||||
|
{@const x = (layoutInfo.size[0] * scale) / 2}
|
||||||
|
{@const y = layoutInfo.size[1] * scale + margin * (flipped ? 2 : 3)}
|
||||||
<text
|
<text
|
||||||
transition:fly={{ y: 2, easing: expoOut }}
|
transition:fly={{ y: 2, easing: expoOut }}
|
||||||
|
transform={flipped ? "rotate(180)" : undefined}
|
||||||
|
transform-origin="{x} {y}"
|
||||||
class="handle-label"
|
class="handle-label"
|
||||||
text-anchor="middle"
|
text-anchor="middle"
|
||||||
font-size={fontSize}
|
font-size={fontSize}
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
x={(layoutInfo.size[0] * scale) / 2}
|
{x}
|
||||||
y={layoutInfo.size[1] * scale + margin * 3}>{rotation - 90}°</text
|
{y}>{rotation - 90}°</text
|
||||||
>
|
>
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
</g>
|
</g>
|
||||||
|
<circle
|
||||||
|
bind:this={rotationTarget}
|
||||||
|
cx={(layoutInfo.rotationAnchor?.[0] ?? 0) * scale}
|
||||||
|
cy={(layoutInfo.rotationAnchor?.[1] ?? 0) * scale}
|
||||||
|
r="0"
|
||||||
|
/>
|
||||||
|
|
||||||
{#each layoutInfo.fixedKeys as key, i}
|
<g
|
||||||
|
transform="rotate({flipped ? 180 : 0})"
|
||||||
|
transform-origin="{(layoutInfo.rotationAnchor?.[0] ?? 0) *
|
||||||
|
scale} {(layoutInfo.rotationAnchor?.[1] ?? 0) * scale}"
|
||||||
|
>
|
||||||
|
{#each layoutInfo.fixedKeys as key}
|
||||||
<KeyboardKey
|
<KeyboardKey
|
||||||
{i}
|
|
||||||
{key}
|
{key}
|
||||||
onfocusin={() => (focusKey = key)}
|
onfocusin={() => (focusKey = key)}
|
||||||
onclick={() => edit(i)}
|
onclick={() => edit(key.id)}
|
||||||
onkeypress={({ key }) => {
|
onkeypress={(event) => {
|
||||||
if (key === "Enter") {
|
if (event.key === "Enter") {
|
||||||
edit(i);
|
edit(key.id);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{/each}
|
{/each}
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|||||||
@@ -17,14 +17,14 @@
|
|||||||
const highlight = getContext<Writable<Set<number>> | undefined>("highlight");
|
const highlight = getContext<Writable<Set<number>> | undefined>("highlight");
|
||||||
|
|
||||||
let {
|
let {
|
||||||
i,
|
|
||||||
key,
|
key,
|
||||||
|
flipped = false,
|
||||||
onclick,
|
onclick,
|
||||||
onkeypress,
|
onkeypress,
|
||||||
onfocusin,
|
onfocusin,
|
||||||
}: {
|
}: {
|
||||||
i: number;
|
|
||||||
key: CompiledLayoutKey;
|
key: CompiledLayoutKey;
|
||||||
|
flipped?: boolean;
|
||||||
onclick: MouseEventHandler<SVGGElement>;
|
onclick: MouseEventHandler<SVGGElement>;
|
||||||
onkeypress: KeyboardEventHandler<SVGGElement>;
|
onkeypress: KeyboardEventHandler<SVGGElement>;
|
||||||
onfocusin: FocusEventHandler<SVGGElement>;
|
onfocusin: FocusEventHandler<SVGGElement>;
|
||||||
@@ -44,7 +44,10 @@
|
|||||||
{onkeypress}
|
{onkeypress}
|
||||||
{onfocusin}
|
{onfocusin}
|
||||||
role="button"
|
role="button"
|
||||||
tabindex={i + 1}
|
tabindex={key.id + 1}
|
||||||
|
transform={flipped ? "rotate(180)" : undefined}
|
||||||
|
transform-origin="center"
|
||||||
|
data-id={key.id}
|
||||||
>
|
>
|
||||||
{#if key.shape === "square"}
|
{#if key.shape === "square"}
|
||||||
<rect
|
<rect
|
||||||
@@ -122,6 +125,9 @@
|
|||||||
path,
|
path,
|
||||||
g {
|
g {
|
||||||
transform-box: fill-box;
|
transform-box: fill-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
path {
|
||||||
transform-origin: top left;
|
transform-origin: top left;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user