['mode', 'target', 'folder', 'path']]: OrganizeCommands::apply() makes the XAPI calls. * * @property IconBox $icon * @property Label $heading * @property Label $listLabel * @property ListBox $folders * @property Label $valueLabel * @property TextBox $value * @property Label $hint * @property Label $error * @property Button $ok * @property Button $cancel */ final class FolderForm extends Form { public const TOP = '(top level)'; public function __construct(private \App\Model\Session $session) { parent::__construct(); } /** XAPI's answers, events and the console go on while this dialog is open. */ protected function tick(float $dt): void { $this->session->tick($dt); } private array $params = []; protected function loaded(array $params = []): void { $this->params = $params; $mode = (string)($params['mode'] ?? 'move'); $move = $mode === 'move'; $this->folders->visible = $this->listLabel->visible = $move; $this->icon->kind = $mode === 'renameTag' ? 'tag' : 'folder'; $this->error->text = ''; $this->value->text = (string)($params['value'] ?? ''); if ($move) { $this->folders->items = [self::TOP, ...array_map('strval', (array)($params['folders'] ?? []))]; $this->folders->selected = max(0, (int)array_search((string)($params['folder'] ?? '') ?: self::TOP, $this->folders->items, true)); } [$heading, $label, $ok, $hint] = match ($mode) { 'new' => ['New Folder', 'Name of the new folder:', 'Create', (($params['parent'] ?? '') !== '' ? "In '" . $params['parent'] . "'. " : '') . 'A folder is kept in the pool\'s settings until an object is moved into it.'], 'rename' => ["Rename Folder '" . ($params['path'] ?? '') . "'", 'New name:', 'Rename', 'Its objects and its subfolders follow it. A path starting with / moves it.'], 'renameTag' => ["Rename Tag '" . ($params['path'] ?? '') . "'", 'New name:', 'Rename', 'Every object that has this tag gets the new one.'], default => ["Move '" . ($params['name'] ?? '') . "' to a folder", 'Or a new folder:', 'Move', 'Folders are kept on the server (other_config "folder"), so every XenCenter sees them.'], }; $this->heading->text = $heading; $this->valueLabel->text = $label; $this->ok->text = $ok; $this->hint->text = $hint; $this->setFocus($move ? $this->folders : $this->value); } public function onOkClick(mixed $sender = null): void { $mode = (string)($this->params['mode'] ?? 'move'); $typed = trim($this->value->text); $path = (string)($this->params['path'] ?? ''); if ($mode !== 'move' && $typed === '') { $this->error->text = 'Enter a name.'; return; } if ($mode === 'renameTag' && str_contains($typed, ',')) { $this->error->text = 'A tag has no comma.'; return; } $folder = match ($mode) { 'move' => $typed !== '' ? Organizer::normalize($typed) : (($v = $this->folders->value()) === null || $v === self::TOP ? '' : $v), 'new' => Organizer::normalize(str_starts_with($typed, '/') ? $typed : (string)($this->params['parent'] ?? '') . '/' . $typed), 'rename' => Organizer::normalize(str_starts_with($typed, '/') ? $typed : Organizer::parent($path) . '/' . $typed), default => $typed, }; if ($mode === 'rename' && ($folder === '' || Organizer::inside($folder, $path))) { $this->error->text = 'A folder cannot go inside itself.'; return; } $this->close(['organize' => ['mode' => $mode, 'target' => $this->params['target'] ?? '', 'folder' => $folder, 'path' => $path]]); } public function onCancelClick(Button $sender): void { $this->close(); } }