Backup
This commit is contained in:
parent
3558819899
commit
37c2a1db67
@ -37,7 +37,8 @@ final class KanbanTaskCard extends Container
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->boardView->selectTask($this->boardId, $this->task);
|
||||
// Start drag operation on click
|
||||
$this->boardView->beginDrag($this->boardId, $this->task, $this);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -90,6 +91,7 @@ final class KanbanBoardView extends Container
|
||||
private array $boardViews = [];
|
||||
private null|string $selectedTaskId = null;
|
||||
private null|string $selectedBoardId = null;
|
||||
private null|array $dragState = null;
|
||||
|
||||
public function __construct(
|
||||
private readonly string $storagePath,
|
||||
@ -158,6 +160,41 @@ final class KanbanBoardView extends Container
|
||||
$this->renderBoards();
|
||||
}
|
||||
|
||||
public function beginDrag(string $boardId, array $task, KanbanTaskCard $card): void
|
||||
{
|
||||
$vp = $card->getViewport();
|
||||
|
||||
$ghost = new Container('bg-yellow-300 border-2 border-red-600 rounded px-3 py-2 shadow-2xl');
|
||||
$ghost->addComponent(new Label($task['title'], 'text-sm text-black'));
|
||||
$ghost->setViewport(clone $vp);
|
||||
$ghost->setContentViewport(clone $vp);
|
||||
$ghost->setOverlay(true);
|
||||
|
||||
$this->addComponent($ghost);
|
||||
|
||||
$this->dragState = [
|
||||
'fromBoardId' => $boardId,
|
||||
'task' => $task,
|
||||
'ghost' => $ghost,
|
||||
];
|
||||
|
||||
$this->statusLabel->setText('Drag gestartet: ' . $task['title']);
|
||||
}
|
||||
|
||||
public function handleDrag(float $mouseX, float $mouseY): void
|
||||
{
|
||||
if ($this->dragState === null) {
|
||||
return;
|
||||
}
|
||||
/** @var Container $ghost */
|
||||
$ghost = $this->dragState['ghost'];
|
||||
$vp = $ghost->getViewport();
|
||||
$vp->x = (int) ($mouseX - $vp->width / 2);
|
||||
$vp->y = (int) ($mouseY - $vp->height / 2);
|
||||
$ghost->setViewport($vp);
|
||||
$ghost->setContentViewport(clone $vp);
|
||||
}
|
||||
|
||||
public function moveSelectedToBoard(string $targetBoardId): void
|
||||
{
|
||||
if ($this->selectedTaskId === null) {
|
||||
@ -218,6 +255,46 @@ final class KanbanBoardView extends Container
|
||||
}
|
||||
}
|
||||
|
||||
public function handleMouseRelease(float $mouseX, float $mouseY, int $button): void
|
||||
{
|
||||
parent::handleMouseRelease($mouseX, $mouseY, $button);
|
||||
|
||||
if ($button !== 1 || $this->dragState === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine drop target board
|
||||
$targetBoardId = null;
|
||||
foreach ($this->boardViews as $boardId => $info) {
|
||||
$vp = $info['column']->getViewport();
|
||||
if (
|
||||
$mouseX >= $vp->x &&
|
||||
$mouseX <= ($vp->x + $vp->width) &&
|
||||
$mouseY >= $vp->y &&
|
||||
$mouseY <= ($vp->y + $vp->height)
|
||||
) {
|
||||
$targetBoardId = $boardId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove ghost
|
||||
if (($this->dragState['ghost'] ?? null) instanceof Container) {
|
||||
$this->dragState['ghost']->setVisible(false);
|
||||
}
|
||||
|
||||
$state = $this->dragState;
|
||||
$this->dragState = null;
|
||||
|
||||
if ($targetBoardId !== null) {
|
||||
$this->selectedBoardId = $state['fromBoardId'];
|
||||
$this->selectedTaskId = $state['task']['id'];
|
||||
$this->moveSelectedToBoard($targetBoardId);
|
||||
} else {
|
||||
$this->statusLabel->setText('Drag abgebrochen.');
|
||||
}
|
||||
}
|
||||
|
||||
private function renderBoards(): void
|
||||
{
|
||||
$this->clearChildren();
|
||||
|
||||
@ -7,26 +7,27 @@
|
||||
"id": "task_691661d89de735.41071535",
|
||||
"title": "Idee sammeln",
|
||||
"note": ""
|
||||
},
|
||||
{
|
||||
"id": "task_691661d89de8e0.86926319",
|
||||
"title": "UI Grundlayout",
|
||||
"note": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "board_691661d89de806.79123800",
|
||||
"title": "In Arbeit",
|
||||
"tasks": []
|
||||
"tasks": [
|
||||
{
|
||||
"id": "task_691661d89de858.46479539",
|
||||
"title": "API anbinden",
|
||||
"note": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "board_691661d89de894.20053237",
|
||||
"title": "Erledigt",
|
||||
"tasks": [
|
||||
{
|
||||
"id": "task_691661d89de858.46479539",
|
||||
"title": "API anbinden",
|
||||
"id": "task_691661d89de8e0.86926319",
|
||||
"title": "UI Grundlayout",
|
||||
"note": ""
|
||||
}
|
||||
]
|
||||
|
||||
@ -344,6 +344,19 @@ abstract class Component
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional: handle drag (mouse move with pressed button).
|
||||
* Default: propagate to children.
|
||||
*/
|
||||
public function handleDrag(float $mouseX, float $mouseY): void
|
||||
{
|
||||
foreach ($this->children as $child) {
|
||||
if (method_exists($child, 'handleDrag')) {
|
||||
$child->handleDrag($mouseX, $mouseY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function layout(null|TextRenderer $textRenderer = null): void
|
||||
{
|
||||
$this->normalStylesCached = StyleParser::parse($this->style)->getValidStyles(
|
||||
|
||||
@ -17,7 +17,7 @@ class Window
|
||||
private Viewport $viewport;
|
||||
private bool $shouldBeReLayouted = true;
|
||||
private float $pixelRatio = 1.0;
|
||||
private float $uiScale = 2.0;
|
||||
private float $uiScale = 1.0;
|
||||
private bool $shouldClose = false;
|
||||
private $onResize = null;
|
||||
private $onFpsChange = null;
|
||||
@ -51,15 +51,19 @@ class Window
|
||||
$flags = SDL_WINDOW_HIGH_PIXEL_DENSITY | SDL_WINDOW_RESIZABLE;
|
||||
|
||||
$this->window = sdl_create_window($title, $width, $height, $flags);
|
||||
|
||||
if (!$this->window) {
|
||||
throw new \Exception('Failed to create window: ' . sdl_get_error());
|
||||
}
|
||||
|
||||
// Get window ID for event routing
|
||||
$this->windowId = sdl_get_window_id($this->window);
|
||||
|
||||
$this->uiScale = sdl_get_window_display_scale($this->window);
|
||||
// Use display scale as UI scale (e.g. 2.0 on HiDPI)
|
||||
if (function_exists('sdl_get_window_display_scale')) {
|
||||
$scale = sdl_get_window_display_scale($this->window);
|
||||
if ($scale > 0.1 && $scale <= 4.0) {
|
||||
$this->uiScale = (float) $scale;
|
||||
}
|
||||
}
|
||||
// Enable text input for this window
|
||||
sdl_start_text_input($this->window);
|
||||
|
||||
@ -237,6 +241,10 @@ class Window
|
||||
case SDL_EVENT_QUIT:
|
||||
case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
|
||||
$this->shouldClose = true;
|
||||
// Drag handling: mouse move while left button is pressed
|
||||
if ($this->leftButtonDown && $this->rootComponent) {
|
||||
$this->rootComponent->handleDrag($this->mouseX, $this->mouseY);
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_EVENT_KEY_DOWN:
|
||||
@ -283,8 +291,9 @@ class Window
|
||||
break;
|
||||
|
||||
case SDL_EVENT_MOUSE_MOTION:
|
||||
$newMouseX = (float) ($event['x'] ?? 0);
|
||||
$newMouseY = (float) ($event['y'] ?? 0);
|
||||
// Convert physical pixels to logical coordinates using uiScale
|
||||
$newMouseX = (float) ($event['x'] ?? 0) / $this->uiScale;
|
||||
$newMouseY = (float) ($event['y'] ?? 0) / $this->uiScale;
|
||||
|
||||
$this->mouseX = $newMouseX;
|
||||
$this->mouseY = $newMouseY;
|
||||
@ -316,6 +325,10 @@ class Window
|
||||
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
||||
$button = $event['button'] ?? 0;
|
||||
|
||||
if ($button === 1) {
|
||||
$this->leftButtonDown = true;
|
||||
}
|
||||
|
||||
// Check overlays first (in reverse z-index order - highest first)
|
||||
if ($this->rootComponent) {
|
||||
$overlays = $this->rootComponent->collectOverlays();
|
||||
@ -342,6 +355,10 @@ class Window
|
||||
case SDL_EVENT_MOUSE_BUTTON_UP:
|
||||
$button = $event['button'] ?? 0;
|
||||
|
||||
if ($button === 1) {
|
||||
$this->leftButtonDown = false;
|
||||
}
|
||||
|
||||
// Propagate release to root component
|
||||
if ($this->rootComponent) {
|
||||
$this->rootComponent->handleMouseRelease($this->mouseX, $this->mouseY, $button);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user