Skip to content

Commit

Permalink
feat(hud customizer): Add resizing
Browse files Browse the repository at this point in the history
  • Loading branch information
wertiop121 committed Aug 8, 2024
1 parent 5aed547 commit 90c68b1
Show file tree
Hide file tree
Showing 3 changed files with 165 additions and 118 deletions.
2 changes: 1 addition & 1 deletion layout/hud/hud.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
</Button>
<NumberEntry id="HudCustomizerGridSize" class="hud-customizer-settings__gridsize" min="4" max="12" onvaluechanged="HudCustomizer.updateGridSize()" />
</Panel>
<Panel id="HudCustomizerKnobContainer" class="hud-customizer__knob-container" hittest="false" />

<Panel id="HudCustomizerGrid" class="hud-customizer__grid" hittest="false" hittestchildren="false" />
</Panel>

Expand Down
268 changes: 160 additions & 108 deletions scripts/hud/customizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,29 @@ namespace HudCustomizer {
TOP_LEFT: 8
};

enum DragMode {
MOVE = 0,
RESIZE_TOP = 1,
RESIZE_TOP_RIGHT = 2,
RESIZE_RIGHT = 3,
RESIZE_BOTTOM_RIGHT = 4,
RESIZE_BOTTOM = 5,
RESIZE_BOTTOM_LEFT = 6,
RESIZE_LEFT = 7,
RESIZE_TOP_LEFT = 8
}

const resizeVector: ReadonlyMap<Exclude<DragMode, DragMode.MOVE>, [number, number]> = new Map([
[DragMode.RESIZE_TOP, [0, -1]],
[DragMode.RESIZE_TOP_RIGHT, [1, -1]],
[DragMode.RESIZE_RIGHT, [1, 0]],
[DragMode.RESIZE_BOTTOM_RIGHT, [1, 1]],
[DragMode.RESIZE_BOTTOM, [0, 1]],
[DragMode.RESIZE_BOTTOM_LEFT, [-1, 1]],
[DragMode.RESIZE_LEFT, [-1, 0]],
[DragMode.RESIZE_TOP_LEFT, [-1, -1]]
]);

// Tuple of X, Y axis snaps respectively
const DEFAULT_SNAP_MODES: Snaps = [SnapMode.OFF, SnapMode.OFF];
const DEFAULT_GRID_SIZE = 5;
Expand All @@ -48,6 +71,7 @@ namespace HudCustomizer {
components: {
[id: string]: {
position: LayoutUtil.Position;
size: LayoutUtil.Size;
snaps: [number, number];
};
};
Expand All @@ -60,6 +84,7 @@ namespace HudCustomizer {
panel: Panel;
snaps: Snaps;
position: LayoutUtil.Position;
size: LayoutUtil.Size;
properties?: Record<string, unknown>;
}

Expand Down Expand Up @@ -133,8 +158,7 @@ namespace HudCustomizer {
virtualName: $<Label>('#HudCustomizerVirtualName')!,
snaps: $('#HudCustomizerSnaps')!,
grid: $('#HudCustomizerGrid')!,
gridSize: $<NumberEntry>('#HudCustomizerGridSize')!,
knobContainer: $('#HudCustomizerKnobContainer')!
gridSize: $<NumberEntry>('#HudCustomizerGridSize')!
};

let components: Record<string, Component>;
Expand All @@ -147,11 +171,7 @@ namespace HudCustomizer {
let dragEndHandle: number | undefined;
let onThinkHandle: number | undefined;

// TODO: Unused. Didn't get very far with resize stuff, behaviour should be
// similar to dragging the virtual panel.
let knobDragStartHandle: number | undefined;
let knobDragEndHandle: number | undefined;
let knobOnThinkHandle: number | undefined;
let dragMode: DragMode | undefined;

let gridSize = 5;

Expand Down Expand Up @@ -179,6 +199,7 @@ namespace HudCustomizer {

saveData.components[id] = {
position: [posX, posY],
size: LayoutUtil.getSize(component.panel),
snaps: component.snaps
};
}
Expand Down Expand Up @@ -208,11 +229,15 @@ namespace HudCustomizer {
Object.values(ResizeDirection)
.filter((x) => !Number.isNaN(+x))
.forEach((dir) => {
resizeKnobs[dir] = $.CreatePanel('Button', panels.knobContainer, `Resize_${ResizeDirection[dir]}`, {
resizeKnobs[dir] = $.CreatePanel('Button', panels.customizer, `Resize_${ResizeDirection[dir]}`, {
class: 'hud-customizer__resize-knob',
draggable: true,
hittest: true
});
$.RegisterEventHandler('DragStart', resizeKnobs[dir], (...args) =>
onStartDrag(dir, resizeKnobs[dir], ...args)
);
$.RegisterEventHandler('DragEnd', resizeKnobs[dir], () => onEndDrag());
});

components = {};
Expand All @@ -222,7 +247,7 @@ namespace HudCustomizer {
.forEach((panel) => loadComponentPanel(layoutData?.components[panel.id], panel));
}

function loadComponentPanel(component: Pick<Component, 'position' | 'snaps'> | undefined, panel: Panel) {
function loadComponentPanel(component: Pick<Component, 'position' | 'size' | 'snaps'> | undefined, panel: Panel) {
if (component) {
for (const [i, snap] of component.snaps.entries()) {
if (!Object.values(SnapMode).includes(snap)) {
Expand All @@ -240,7 +265,7 @@ namespace HudCustomizer {
const isX = i === 0;
const max = isX ? 1920 : 1080;
const sf = snaps[i][component.snaps[i]].sizeFactor;
const panelLen = isX ? LayoutUtil.getWidth(panel) : LayoutUtil.getHeight(panel);
const panelLen = component.size[i];
let layoutLen = len - panelLen * sf;
const maxOOB = 16;

Expand All @@ -259,7 +284,7 @@ namespace HudCustomizer {
return layoutLen;
}) as LayoutUtil.Position;

LayoutUtil.setPosition(panel, layoutPos);
LayoutUtil.setPositionAndSize(panel, layoutPos, component.size);

components[panel.id] = {
panel,
Expand Down Expand Up @@ -294,6 +319,7 @@ namespace HudCustomizer {
components[panel.id] = {
panel,
position: LayoutUtil.getPosition(panel),
size: size,
snaps: DEFAULT_SNAP_MODES
};
}
Expand Down Expand Up @@ -325,21 +351,109 @@ namespace HudCustomizer {
$.UnregisterEventHandler('DragStart', panels.virtual, dragStartHandle);
}

function onComponentMouseOver(component: Component) {
if (activeComponent && activeComponent === component) return;
function onStartDrag(mode, displayPanel, _source, callback) {
if (!activeComponent) return;

activeComponent?.panel.RemoveClass('hud-customizable--active');
dragMode = mode;

activeComponent = component;
onThinkHandle = $.RegisterEventHandler('HudThink', $.GetContextPanel(), () => onDragThink());

activeComponent.panel.AddClass('hud-customizable--active');
if (mode !== DragMode.MOVE) {
callback.offsetX = 0;
callback.offsetY = 0;
}

const [[x, y], [width, height]] = LayoutUtil.getPositionAndSize(component.panel);
callback.displayPanel = displayPanel;
callback.removePositionBeforeDrop = false;
}

// Set the virtual panel's position and size to the component we just hovered over
LayoutUtil.setPositionAndSize(panels.virtual, [x, y], [width, height]);
LayoutUtil.setPositionAndSize(panels.knobContainer, [x, y], [width, height]);
function onEndDrag() {
if (!activeComponent) return;

onDragThink();

$.UnregisterEventHandler('HudThink', $.GetContextPanel(), onThinkHandle);

LayoutUtil.setPositionAndSize(panels.virtual, activeComponent.position, activeComponent.size);
updateResizeKnobs(activeComponent.position, activeComponent.size);

activeComponent.panel.RemoveClass('hud-customizable--dragging');
panels.virtual.RemoveClass('hud-customizer-virtual--dragging');

activeGridlines?.forEach((line) => line?.panel.RemoveClass('hud-customizer__gridline--highlight'));
activeGridlines = [undefined, undefined];

// TODO: this is just for testing
save();

dragMode = undefined;
}

function onDragThink() {
if (!activeComponent || dragMode === undefined) return;

let panelPos = activeComponent.position;
const panelSize = activeComponent.size;

if (dragMode === DragMode.MOVE) {
panelPos = LayoutUtil.getPosition(panels.virtual);
} else {
const resizeDir = resizeVector.get(dragMode) ?? [0, 0];
const knobPos = LayoutUtil.getPosition(resizeKnobs[dragMode]);
if (resizeDir[0] === 1) {
panelSize[0] = knobPos[0] - panelPos[0];
} else if (resizeDir[0] === -1) {
panelSize[0] += panelPos[0] - knobPos[0];
panelPos[0] = knobPos[0];
}

if (resizeDir[1] === 1) {
panelSize[1] = knobPos[1] - panelPos[1];
} else if (resizeDir[1] === -1) {
panelSize[1] += panelPos[1] - knobPos[1];
panelPos[1] = knobPos[1];
}
}

const stupidFontSizeThing = Math.min(panelSize[1] / 2, panelSize[0] / 2);
panels.virtualName.style.fontSize = stupidFontSizeThing;

LayoutUtil.setPositionAndSize(panels.virtual, panelPos, panelSize);

// snapping
if (dragMode === DragMode.MOVE) {
for (const axis of Axes) {
const isX = axis === 0;

if (activeComponent.snaps[axis] !== SnapMode.OFF) {
const sizeFactor = snaps[axis][activeComponent.snaps[axis]].sizeFactor;

const offset = panelSize[axis] * sizeFactor;

const gridline = getNearestGridLine(axis, sizeFactor);
const activeGridline = activeGridlines[axis];

if (gridline) panelPos[axis] = gridline.offset - offset;
if (gridline !== activeGridline) {
if (activeGridline) activeGridline.panel.RemoveClass('hud-customizer__gridline--highlight');
if (gridline) {
gridline.panel.AddClass('hud-customizer__gridline--highlight');
activeGridlines[axis] = gridline;
}
}
}
}
}

activeComponent.position = panelPos;
activeComponent.size = panelSize;
LayoutUtil.setPositionAndSize(activeComponent.panel, activeComponent.position, activeComponent.size);
updateResizeKnobs(panelPos, panelSize);
}

function updateResizeKnobs(position, size) {
const width = size[0];
const height = size[1];
const halfWidth = width / 2;
const halfHeight = height / 2;
let plusX, plusY;
Expand Down Expand Up @@ -379,10 +493,25 @@ namespace HudCustomizer {
break;
}

$.Msg({ x, y, width, height, dir, plusX, plusY });

LayoutUtil.setPosition(knob, [plusX, plusY]);
LayoutUtil.setPosition(knob, [position[0] + plusX, position[1] + plusY]);
}
}

function onComponentMouseOver(component: Component) {
if (activeComponent && activeComponent === component) return;

activeComponent?.panel.RemoveClass('hud-customizable--active');

activeComponent = component;

activeComponent.panel.AddClass('hud-customizable--active');

const [position, size] = LayoutUtil.getPositionAndSize(component.panel);

// Set the virtual panel's position and size to the component we just hovered over
LayoutUtil.setPositionAndSize(panels.virtual, position, size);

updateResizeKnobs(position, size);

// This is going to be weird to localise, but I guess we could do it, probably in V2 when we can
// define the locale string in the `customisable` XML property.
Expand All @@ -397,96 +526,19 @@ namespace HudCustomizer {
panels.virtualName.style.fontSize = stupidFontSizeThing;

snaps[0][activeComponent.snaps[0]].button.SetSelected(true);
snaps[1][activeComponent.snaps[1]].button.SetSelected(true);
snaps[1][activeComponent.snaps[1]].button.SetSelected(true);

if (dragStartHandle) {
$.UnregisterEventHandler('DragStart', panels.virtual, dragStartHandle);
}

dragStartHandle = $.RegisterEventHandler('DragStart', panels.virtual, (...args) => onStartDrag(...args));
}

function onStartDrag(_source, callback) {
if (!activeComponent) return;

callback.displayPanel = panels.virtual;
callback.removePositionBeforeDrop = false;

// These aren't actually related to one-another in XML hierarchy so need to handle two classes
activeComponent.panel.AddClass('hud-customizable--dragging');
panels.virtual.AddClass('hud-customizer-virtual--dragging');

$.UnregisterEventHandler('DragStart', panels.virtual, dragStartHandle);

onThinkHandle = $.RegisterEventHandler('HudThink', $.GetContextPanel(), () => onDragThink());

dragEndHandle = $.RegisterEventHandler('DragEnd', panels.virtual, () => onEndDrag());
}

function onDragThink() {
if (!activeComponent) return;

const oldPosition = [0, 0];
const newPosition: LayoutUtil.Position = [0, 0];

for (const axis of Axes) {
const isX = axis === 0;

if (activeComponent.snaps[axis] === SnapMode.OFF) {
newPosition[axis] = isX ? LayoutUtil.getX(panels.virtual) : LayoutUtil.getY(panels.virtual);
} else {
const sizeFactor = snaps[axis][activeComponent.snaps[axis]].sizeFactor;

const offset = isX
? (activeComponent.panel.actuallayoutwidth * sizeFactor) / scaleX
: (activeComponent.panel.actuallayoutheight * sizeFactor) / scaleY;

const gridline = getNearestGridLine(axis, sizeFactor);
const activeGridline = activeGridlines[axis];

if (activeGridline) oldPosition[axis] = activeGridline.offset - offset;
if (gridline) newPosition[axis] = gridline.offset - offset;

if (gridline !== activeGridline) {
if (activeGridline) activeGridline.panel.RemoveClass('hud-customizer__gridline--highlight');
if (gridline) {
gridline.panel.AddClass('hud-customizer__gridline--highlight');
activeGridlines[axis] = gridline;

newPosition[axis] = gridline.offset - offset;
}
}
}
if (dragEndHandle) {
$.UnregisterEventHandler('DragEnd', panels.virtual, dragEndHandle);
}

if (newPosition[0] !== oldPosition[1] || newPosition[1] !== oldPosition[1]) {
LayoutUtil.setPosition(activeComponent.panel, newPosition);
LayoutUtil.setPosition(panels.knobContainer, newPosition);
}
}

function onEndDrag() {
if (!activeComponent) return;

$.UnregisterEventHandler('DragEnd', panels.virtual, dragEndHandle);
$.UnregisterEventHandler('HudThink', $.GetContextPanel(), onThinkHandle);

dragStartHandle = $.RegisterEventHandler('DragStart', panels.virtual, (...args) => onStartDrag(...args));

dragEndHandle = undefined;
onThinkHandle = undefined;

activeComponent.panel.RemoveClass('hud-customizable--dragging');
panels.virtual.RemoveClass('hud-customizer-virtual--dragging');

LayoutUtil.copyPositionAndSize(activeComponent.panel, panels.virtual);

activeGridlines?.forEach((line) => line?.panel.RemoveClass('hud-customizer__gridline--highlight'));

activeGridlines = [undefined, undefined];

// TODO: this is just for testing
save();
dragStartHandle = $.RegisterEventHandler('DragStart', panels.virtual, (...args) =>
onStartDrag(DragMode.MOVE, panels.virtual, ...args)
);
dragEndHandle = $.RegisterEventHandler('DragEnd', panels.virtual, () => onEndDrag());
}

function getNearestGridLine(axis: Axis, sizeFactor: number): Gridline {
Expand Down Expand Up @@ -552,7 +604,7 @@ namespace HudCustomizer {
if (newSize !== gridSize) {
gridSize = newSize;
createGridLines();
}
}
}

export function setSnapMode(axis: keyof Axis, mode: SnapMode): void {
Expand Down
Loading

0 comments on commit 90c68b1

Please sign in to comment.