create basic radial menu

This commit is contained in:
evanpelle
2024-09-26 16:24:58 -07:00
parent e5bcdb986f
commit 851e12613a
10 changed files with 1055 additions and 92 deletions
+3 -2
View File
@@ -3,12 +3,12 @@ import {NameLayer} from "./layers/NameLayer";
import {TerrainLayer} from "./layers/TerrainLayer";
import {TerritoryLayer} from "./layers/TerritoryLayer";
import {ClientID} from "../../core/Schemas";
import {createCanvas} from "./Utils";
import {UILayer} from "./layers/UILayer";
import {EventBus} from "../../core/EventBus";
import {TransformHandler} from "./TransformHandler";
import {Layer} from "./layers/Layer";
import {EventsDisplay} from "./layers/EventsDisplay";
import {RadialMenu} from "./layers/RadialMenu";
export function createRenderer(canvas: HTMLCanvasElement, game: Game, eventBus: EventBus, clientID: ClientID): GameRenderer {
@@ -19,7 +19,8 @@ export function createRenderer(canvas: HTMLCanvasElement, game: Game, eventBus:
new TerritoryLayer(game, eventBus),
new NameLayer(game, game.config().theme(), transformHandler, clientID),
new UILayer(eventBus, game, clientID, transformHandler),
new EventsDisplay(eventBus, game, clientID)
new EventsDisplay(eventBus, game, clientID),
new RadialMenu(eventBus),
]
return new GameRenderer(game, eventBus, canvas, transformHandler, layers)
+131
View File
@@ -0,0 +1,131 @@
import {EventBus} from "../../../core/EventBus";
import {ContextMenuEvent} from "../../InputHandler";
import {Layer} from "./Layer";
import * as d3 from 'd3';
export class RadialMenu implements Layer {
private menuElement: d3.Selection<HTMLDivElement, unknown, null, undefined>;
private isVisible: boolean = false;
private readonly menuItems = [
{name: "sub", color: "#3498db"},
{name: "color", color: "#e74c3c"},
{name: "shape", color: "#2ecc71"},
{name: "font", color: "#f39c12"},
{name: "stroke", color: "#9b59b6"}
];
private readonly menuSize = 300; // Increased size
private readonly centerButtonSize = 60;
constructor(private eventBus: EventBus) { }
init() {
this.eventBus.on(ContextMenuEvent, e => this.onContextMenu(e))
this.createMenuElement();
}
private createMenuElement() {
this.menuElement = d3.select(document.body)
.append('div')
.style('position', 'fixed')
.style('display', 'none')
.style('z-index', '9999')
.style('touch-action', 'none'); // Prevent default touch actions
const svg = this.menuElement.append('svg')
.attr('width', this.menuSize)
.attr('height', this.menuSize)
.append('g')
.attr('transform', `translate(${this.menuSize / 2},${this.menuSize / 2})`);
const pie = d3.pie<any>()
.value(() => 1)
.padAngle(0.03);
const arc = d3.arc<any>()
.innerRadius(this.centerButtonSize + 10)
.outerRadius(this.menuSize / 2 - 10);
const arcs = svg.selectAll('path')
.data(pie(this.menuItems))
.enter()
.append('g');
arcs.append('path')
.attr('d', arc)
.attr('fill', d => d.data.color)
.on('click', (event, d) => this.handleMenuSelection(d.data.name))
.on('touchstart', (event, d) => {
event.preventDefault();
this.handleMenuSelection(d.data.name);
});
arcs.append('text')
.attr('transform', d => `translate(${arc.centroid(d)})`)
.attr('text-anchor', 'middle')
.attr('fill', 'white')
.style('font-size', '14px')
.text(d => d.data.name);
// Add center button
svg.append('circle')
.attr('r', this.centerButtonSize)
.attr('fill', '#2c3e50')
.on('click', () => this.handleCenterButtonClick())
.on('touchstart', (event) => {
event.preventDefault();
this.handleCenterButtonClick();
});
svg.append('text')
.attr('text-anchor', 'middle')
.attr('dy', '0.3em')
.attr('fill', 'white')
.style('font-size', '14px')
.text('Close');
}
tick() {
// Update logic if needed
}
render(context: CanvasRenderingContext2D) {
// No need to render anything on the canvas
}
shouldTransform(): boolean {
return false;
}
private onContextMenu(event: ContextMenuEvent) {
console.log('on context menu')
if (this.isVisible) {
this.hideRadialMenu()
} else {
this.showRadialMenu(event.x, event.y);
}
}
private showRadialMenu(x: number, y: number) {
this.menuElement
.style('left', `${x - this.menuSize / 2}px`)
.style('top', `${y - this.menuSize / 2}px`)
.style('display', 'block');
this.isVisible = true;
}
private hideRadialMenu() {
this.menuElement.style('display', 'none');
this.isVisible = false;
}
private handleMenuSelection(action: string) {
console.log(`Selected action: ${action}`);
this.hideRadialMenu();
}
private handleCenterButtonClick() {
console.log('Center button clicked');
this.hideRadialMenu();
}
}
+3 -3
View File
@@ -3,7 +3,7 @@ import {EventBus, GameEvent} from "../../../core/EventBus";
import {WinEvent} from "../../../core/execution/WinCheckExecution";
import {AllianceRequest, AllianceRequestReplyEvent, Game, Player} from "../../../core/game/Game";
import {ClientID} from "../../../core/Schemas";
import {RightClickEvent} from "../../InputHandler";
import {ContextMenuEvent} from "../../InputHandler";
import {Layer} from "./Layer";
import {TransformHandler} from "../TransformHandler";
import {MessageType} from "./EventsDisplay";
@@ -74,7 +74,7 @@ export class UILayer implements Layer {
this.createWinModal()
this.initRightClickMenu()
this.eventBus.on(WinEvent, (e) => this.onWinEvent(e))
this.eventBus.on(RightClickEvent, (e) => this.onRightClick(e))
this.eventBus.on(ContextMenuEvent, (e) => this.onRightClick(e))
}
initRightClickMenu() {
@@ -236,7 +236,7 @@ export class UILayer implements Layer {
window.location.reload();
}
private onRightClick(e: RightClickEvent) {
private onRightClick(e: ContextMenuEvent) {
const cell = this.transformHandler.screenToWorldCoordinates(e.x, e.y)
if (!this.game.isOnMap(cell)) {
return