feat: editing

This commit is contained in:
2023-11-02 00:16:18 +01:00
parent fade2f978e
commit ef309d603e
22 changed files with 409 additions and 517 deletions

View File

@@ -1,7 +1,7 @@
<script lang="ts">
import {compileLayout} from "$lib/serialization/visual-layout"
import type {VisualLayout, CompiledLayoutKey} from "$lib/serialization/visual-layout"
import {changes, layout} from "$lib/serial/connection"
import {deviceLayout} from "$lib/serial/connection"
import {dev} from "$app/environment"
import ActionSelector from "$lib/components/layout/ActionSelector.svelte"
import {get} from "svelte/store"
@@ -9,6 +9,7 @@
import KeyboardKey from "$lib/components/layout/KeyboardKey.svelte"
import {getContext} from "svelte"
import type {VisualLayoutConfig} from "./visual-layout.js"
import {changes, ChangeType} from "$lib/undo-redo"
const {scale, margin, strokeWidth, fontSize, iconFontSize} =
getContext<VisualLayoutConfig>("visual-layout-config")
@@ -114,7 +115,7 @@
const clickedGroup = groupParent.children.item(index) as SVGGElement
const component = new ActionSelector({
target: document.body,
props: {currentAction: get(layout)[get(activeLayer)][keyInfo.id]},
props: {currentAction: get(deviceLayout)[get(activeLayer)][keyInfo.id]},
})
const dialog = document.querySelector("dialog > div") as HTMLDivElement
const backdrop = document.querySelector("dialog") as HTMLDialogElement
@@ -152,7 +153,12 @@
component.$on("close", closed)
component.$on("select", ({detail}) => {
changes.update(changes => {
changes.push({layout: {[get(activeLayer)]: {[keyInfo.id]: detail}}})
changes.push({
type: ChangeType.Layout,
id: keyInfo.id,
layer: get(activeLayer),
action: detail,
})
return changes
})
closed()

View File

@@ -1,18 +0,0 @@
<script lang="ts">
import {layout} from "$lib/serial/connection"
import {KEYMAP_CODES} from "$lib/serial/keymap-codes"
import ActionSelector from "$lib/components/layout/ActionSelector.svelte"
import {popup} from "$lib/popup"
import ActionListItem from "$lib/components/ActionListItem.svelte"
export let id: number = 0
</script>
<table>
{#each $layout as layer, i}
<tr>
<th class="icon">counter_{i + 1}</th>
<ActionListItem id={layer[id]} />
</tr>
{/each}
</table>

View File

@@ -1,10 +1,10 @@
<script lang="ts">
import {getActions} from "$lib/components/layout/get-actions.js"
import {changes, layout} from "$lib/serial/connection.js"
import {getContext} from "svelte"
import type {Writable} from "svelte/store"
import type {VisualLayoutConfig} from "$lib/components/layout/visual-layout"
import type {CompiledLayoutKey} from "$lib/serialization/visual-layout"
import {layout} from "$lib/undo-redo.js"
import {KEYMAP_CODES} from "$lib/serial/keymap-codes"
const {fontSize, margin, inactiveOpacity, inactiveScale, iconFontSize} =
getContext<VisualLayoutConfig>("visual-layout-config")
@@ -21,27 +21,28 @@
</script>
{#each positions as position, layer}
{@const [action, changed] = getActions(layer, key.id, $layout, $changes)}
{@const {action: actionId, isApplied} = $layout[layer][key.id]}
{@const {code, icon, id} = KEYMAP_CODES[actionId] ?? {code: actionId}}
{@const isActive = layer === $activeLayer}
{@const direction = [(middle[0] - margin * 3) / position[0], (middle[1] - margin * 3) / position[1]]}
<text
fill={changed ? "var(--md-sys-color-primary)" : "currentcolor"}
font-weight={changed ? "bold" : ""}
fill={isApplied ? "currentcolor" : "var(--md-sys-color-primary)"}
font-weight={isApplied ? "" : "bold"}
text-anchor="middle"
alignment-baseline="central"
x={pos[0] + middle[0] + (changed ? fontSize / 3 : 0)}
x={pos[0] + middle[0] + (isApplied ? 0 : fontSize / 3)}
y={pos[1] + middle[1]}
font-size={fontSizeMultiplier * (action.icon ? iconFontSize : fontSize)}
font-family={action.icon ? "Material Symbols Rounded" : undefined}
font-size={fontSizeMultiplier * (icon ? iconFontSize : fontSize)}
font-family={icon ? "Material Symbols Rounded" : undefined}
opacity={isActive ? 1 : inactiveOpacity}
style:scale={isActive ? 1 : inactiveScale}
style:translate={isActive ? "0 0 0" : `${direction[0]}px ${direction[1]}px 0`}
style:rotate="{rotate}deg"
>
{#if action.code !== 0}
{action.icon || action.id || `0x${action.code?.toString(16)}`}
{#if code !== 0}
{icon || id || `0x${code.toString(16)}`}
{/if}
{#if changed}
{#if !isApplied}
<tspan></tspan>
{/if}
</text>

View File

@@ -44,7 +44,6 @@
{@const multiplier = 1.25}
<g style:transform="rotateZ({key.rotate}deg) translate({innerMargin}px, {innerMargin}px)">
<path
opacity="0.4"
d="M{posX + p1},{posY} a{r1},{r1} 0 0,1 {-p1},{p1} l0,{-(p1 - p2)} a{r2},{r2} 0 0,0 {p2},{-p2}z"
/>
<KeyText
@@ -86,8 +85,8 @@
path {
fill: currentcolor;
fill-opacity: 0.2;
stroke-opacity: 0.6;
fill-opacity: 0;
stroke-opacity: 0.3;
}
g:hover {

View File

@@ -1,65 +0,0 @@
<script>
import RingInput from "$lib/components/layout/RingInput.svelte"
export let activeLayer = 0
</script>
<div class="col layout" style="gap: 0">
<div class="row" style="gap: 156px">
<div class="row">
<RingInput {activeLayer} keys={{d: 30, e: 31, n: 32, w: 33, s: 34}} />
<div class="col">
<RingInput {activeLayer} keys={{d: 25, e: 26, n: 27, w: 28, s: 29}} />
<RingInput {activeLayer} keys={{d: 40, e: 41, n: 42, w: 43, s: 44}} />
</div>
<div class="col">
<RingInput {activeLayer} keys={{d: 20, e: 21, n: 22, w: 23, s: 24}} />
<RingInput {activeLayer} keys={{d: 35, e: 36, n: 37, w: 38, s: 39}} />
</div>
<RingInput {activeLayer} keys={{d: 15, e: 16, n: 17, w: 18, s: 19}} />
</div>
<div class="row">
<RingInput {activeLayer} keys={{d: 60, w: 61, n: 62, e: 63, s: 64}} />
<div class="col">
<RingInput {activeLayer} keys={{d: 65, w: 66, n: 67, e: 68, s: 69}} />
<RingInput {activeLayer} keys={{d: 80, w: 81, n: 82, e: 83, s: 84}} />
</div>
<div class="col">
<RingInput {activeLayer} keys={{d: 70, w: 71, n: 72, e: 73, s: 74}} />
<RingInput {activeLayer} keys={{d: 85, w: 86, n: 87, e: 88, s: 89}} />
</div>
<RingInput {activeLayer} keys={{d: 75, w: 76, n: 77, e: 78, s: 79}} />
</div>
</div>
<div class="row" style="gap: 48px; margin-top: -32px">
<RingInput {activeLayer} keys={{d: 10, e: 11, n: 12, w: 13, s: 14}} />
<RingInput {activeLayer} keys={{d: 55, w: 56, n: 57, e: 58, s: 59}} />
</div>
<div class="row" style="gap: 160px">
<RingInput {activeLayer} keys={{d: 5, e: 6, n: 7, w: 8, s: 9}} />
<RingInput {activeLayer} keys={{d: 50, w: 51, n: 52, e: 53, s: 54}} />
</div>
<div class="row" style="gap: 320px; margin-top: -12px">
<RingInput {activeLayer} keys={{d: 0, e: 1, n: 2, w: 3, s: 4}} />
<RingInput {activeLayer} keys={{d: 45, w: 46, n: 47, e: 48, s: 49}} />
</div>
</div>
<style lang="scss">
.row,
.col {
display: flex;
gap: 8px;
align-items: center;
justify-content: center;
}
.row {
flex-direction: row;
}
.col {
flex-direction: column;
}
</style>

View File

@@ -1,186 +0,0 @@
<script lang="ts">
import {changes, highlightActions, layout} from "$lib/serial/connection"
import type {Change} from "$lib/serial/connection"
import type {CharaLayout} from "$lib/serialization/layout"
import {KEYMAP_CODES} from "$lib/serial/keymap-codes"
import type {KeyInfo} from "$lib/serial/keymap-codes"
import {editableLayout} from "$lib/editable-layout"
export let activeLayer = 0
export let keys: Record<"d" | "s" | "n" | "w" | "e", number>
const virtualLayerMap = [1, 0, 2]
const characterOffset = 8
function offsetDistance(quadrant: number, layer: number, activeLayer: number): number {
const layerOffsetIndex = virtualLayerMap[layer] - virtualLayerMap[activeLayer]
const layerOffset = quadrant > 2 ? -characterOffset : characterOffset
return 25 * quadrant + layerOffsetIndex * layerOffset
}
function getActions(id: number, layout: CharaLayout, changes: Change[]): [KeyInfo, KeyInfo | undefined][] {
return Array.from({length: 3}).map((_, i) => {
const actionId = layout?.[i][id]
const changedId = changes.findLast(it => it?.layout?.[i]?.[id] !== undefined)?.layout![i]![id]
if (changedId !== undefined) {
return [KEYMAP_CODES[changedId], KEYMAP_CODES[actionId]]
} else {
return [KEYMAP_CODES[actionId], undefined]
}
})
}
</script>
<div class="radial">
{#each [keys.n, keys.e, keys.s, keys.w, keys.d] as id, quadrant}
{@const actions = getActions(id, $layout, $changes)}
<button
use:editableLayout={{activeLayer, id}}
class:active={actions.some(([it]) => it && $highlightActions?.includes(it.code))}
>
{#each actions as [keyInfo, old], layer}
{#if keyInfo}
<span
class:active={virtualLayerMap[activeLayer] === virtualLayerMap[layer]}
class:icon={!!keyInfo.icon}
class:changed={!!old}
style="offset-distance: {offsetDistance(quadrant, layer, activeLayer)}%"
>{keyInfo.icon || keyInfo.id || keyInfo.code}</span
>
{/if}
{/each}
</button>
{/each}
</div>
<style lang="scss">
@use "sass:math";
$border-width: 18px;
$gap: 6px;
$size: 96;
$offset: 14;
$scale-difference: 0.2;
$transition-time: 750ms;
.radial {
position: relative;
container: radial / size;
width: #{$size * 1px};
height: #{$size * 1px};
transition: all 250ms ease;
}
span {
$cr: math.div($size, 2) - 2 * $offset;
will-change: scale, offset-distance;
user-select: none;
scale: 0.9;
offset-path: path(
"M#{math.div($size, 2)} #{$offset}A#{$cr} #{$cr} 0 1 1 #{math.div($size, 2)} #{$size - $offset}A#{$cr} #{$cr} 0 1 1 #{math.div($size, 2)} #{$offset}Z"
);
offset-rotate: 0deg;
display: flex;
grid-column: 1;
grid-row: 1;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
font-size: 16px;
opacity: 0.2;
transition:
scale $transition-time ease,
opacity $transition-time ease,
offset-distance $transition-time ease;
&.active {
scale: 1;
opacity: 1;
}
&.icon {
font-size: 20px;
font-weight: 800;
}
&.changed {
color: var(--md-sys-color-on-secondary-container);
background: var(--md-sys-color-secondary-container);
}
}
button {
all: unset;
cursor: pointer;
position: absolute;
display: grid;
width: 100cqw;
height: 100cqh;
padding: 0;
font-family: inherit;
font-size: 16px;
font-weight: 900;
color: var(--md-sys-color-on-surface-variant);
background: var(--md-sys-color-surface-variant);
border: none;
transition: all 250ms ease;
mask-image: url("$lib/assets/quater-ring.svg");
mask-size: 100% 100%;
&.active,
&:active {
color: var(--md-sys-color-on-tertiary);
background: var(--md-sys-color-tertiary);
}
&:nth-child(1) {
clip-path: polygon(50% 50%, 0 0, 100% 0);
}
&:nth-child(2) {
clip-path: polygon(50% 50%, 100% 0, 100% 100%);
}
&:nth-child(3) {
clip-path: polygon(50% 50%, 0 100%, 100% 100%);
}
&:nth-child(4) {
clip-path: polygon(50% 50%, 0 0, 0 100%);
}
&:last-child {
top: 50%;
left: 50%;
translate: -50% -50%;
overflow: hidden;
width: 25cqw;
height: 25cqh;
border-radius: 50%;
mask-image: none;
}
}
</style>

View File

@@ -1,19 +0,0 @@
import type {CharaLayout} from "$lib/serialization/layout"
import type {Change} from "$lib/serial/connection"
import {KEYMAP_CODES} from "$lib/serial/keymap-codes"
import type {KeyInfo} from "$lib/serial/keymap-codes"
export function getActions(
layer: number,
id: number,
layout: CharaLayout,
changes: Change[],
): [KeyInfo, boolean] {
const actionId = layout?.[layer][id]
const changedId = changes.findLast(it => it?.layout?.[layer]?.[id] !== undefined)?.layout?.[layer]?.[id]
if (changedId !== undefined) {
return [KEYMAP_CODES[changedId] ?? {code: changedId}, true]
} else {
return [KEYMAP_CODES[actionId] ?? {code: actionId}, false]
}
}