diff --git a/README.md b/README.md index 6ba6e6f..7966dcb 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,11 @@ # DevelNext-ProjectViewer ### **Для чего ?** -Для просмотра заархивированных проектов DevelNext к которым вряд ли кто-то когда либо вернется, но там есть разные интересные реализации какого то кода. +Для просмотра заархивированных проектов DevelNext без открытия их в самом DevelNext. +А для тех, кто будет использовать еще один плюс — можно посмотреть какие пакеты используются в проекте, и если я знаю про эти пакеты, то будет ссылка на их скачивание либо на страничку GitHub ### **Зачем?** Программа создавалась для личного использования, и не претендует на какой то ОЧЕНЬ серьезный софт. Все что на данный момент реализованно это следствие активного использования этой программы, и то что показалось очень необходимым если этим кто-то будет пользоваться кроме меня. -![ScreenShot](https://raw.githubusercontent.com/silentdeath76/DevelNext-ProjectViewer/main/assets/screenshot1.png) - -![ScreenShot](https://raw.githubusercontent.com/silentdeath76/DevelNext-ProjectViewer/main/assets/screenshot2.png) \ No newline at end of file +![ScreenShot](https://raw.githubusercontent.com/silentdeath76/DevelNext-ProjectViewer/main/assets/screenshot1.png) \ No newline at end of file diff --git a/assets/screenshot1.png b/assets/screenshot1.png index e46e4d8..ad5fa31 100644 Binary files a/assets/screenshot1.png and b/assets/screenshot1.png differ diff --git a/assets/screenshot2.png b/assets/screenshot2.png deleted file mode 100644 index e2b0de4..0000000 Binary files a/assets/screenshot2.png and /dev/null differ diff --git a/src/.data/dependencys.json b/src/.data/dependencys.json index 431c5fa..b7e393a 100644 --- a/src/.data/dependencys.json +++ b/src/.data/dependencys.json @@ -47,5 +47,11 @@ }, { "name": "AudioBundle", "link": "https://github.com/MWGuy/jphp-voice-recorder-ext" + }, { + "name": "DiscordRPC", + "link": "https://github.com/silentdeath76/JPHP-DiscordRPC-ext" + }, { + "name": "Yandex Disc", + "link": "https://github.com/silentdeath76/jphp-yandex-disc" } ] \ No newline at end of file diff --git a/src/.data/web/dark.css b/src/.data/web/dark.css index aa1e0b4..6ddfedc 100644 --- a/src/.data/web/dark.css +++ b/src/.data/web/dark.css @@ -30,7 +30,8 @@ pre .dec { color: #98fb98 !important; } /* decimal - lightgreen */ ol.linenums { margin-top: 0; margin-bottom: 0; - width: 30px; + margin-left: 20px; + min-width: 30px; color: #AEAEAE !important; } /* IE indents via margin-left */ diff --git a/src/.data/web/light.css b/src/.data/web/light.css index 2936013..7f83c19 100644 --- a/src/.data/web/light.css +++ b/src/.data/web/light.css @@ -2,7 +2,7 @@ body { padding: 0px; margin: 0px; - background-color: #FFFFFF !important; + background-color: #fafbfc !important; margin-right: 5px; } @@ -12,7 +12,7 @@ pre { } /* desert scheme ported from vim to google prettify */ -pre.prettyprint { display: block; background-color: #FFFFFF !important; } +pre.prettyprint { display: block; background-color: #fafbfc !important; } pre .nocode { background-color: none; color: #000 !important; } pre .str { color: #925959 !important; } /* string - pink */ pre .kwd { color: #7f7948 !important; font-weight: bold } @@ -30,7 +30,8 @@ pre .dec { color: #249324 !important; } /* decimal - lightgreen */ ol.linenums { margin-top: 0; margin-bottom: 0; - width: 30px; + margin-left: 20px; + min-width: 30px; color: #444 !important; } /* IE indents via margin-left */ @@ -55,11 +56,11 @@ ul { li.L0, li.L1, li.L2, li.L3, li.L4, li.L5, li.L6, li.L7, li.L8, li.L9 { list-style-type: decimal !important; - background-color: #FFFFFF !important; + background-color: #fafbfc !important; user-select: none !important; } -::-webkit-scrollbar-corner { background: #FFFFFF !important; } +::-webkit-scrollbar-corner { background: #fafbfc !important; } ::-moz-selection { background: #F2F2F2 !important; } ::selection { background: #F2F2F2 !important; } \ No newline at end of file diff --git a/src/.data/web/nord.css b/src/.data/web/nord.css index 93db43a..7f74992 100644 --- a/src/.data/web/nord.css +++ b/src/.data/web/nord.css @@ -30,7 +30,8 @@ pre .dec { color: #98fb98 !important; } /* decimal - lightgreen */ ol.linenums { margin-top: 0; margin-bottom: 0; - width: 30px; + margin-left: 20px; + min-width: 30px; color: #AEAEAE !important; } /* IE indents via margin-left */ diff --git a/src/.theme/light.theme.fx.css b/src/.theme/light.theme.fx.css index 6be90f5..a26a4b8 100644 --- a/src/.theme/light.theme.fx.css +++ b/src/.theme/light.theme.fx.css @@ -1,6 +1,6 @@ * { - -base-color: #f0f0f0; - -second-base-color: white; + -base-color: #f7f8fa; + -second-base-color: #fafbfc; -text-color: black; -second-text-color: #444444; -tab-selected-border-color: skyblue; diff --git a/src/.theme/menu-bar.fx.css b/src/.theme/menu-bar.fx.css index 06dab92..fb2a47e 100644 --- a/src/.theme/menu-bar.fx.css +++ b/src/.theme/menu-bar.fx.css @@ -1,5 +1,5 @@ .menu-bar { - -fx-background-color: -base-color; + -fx-background-color: -second-base-color; -fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.3), 5, 0.5, 0.0, 0.0); } diff --git a/src/.theme/tree.fx.css b/src/.theme/tree.fx.css index 47ff889..99cd4e1 100644 --- a/src/.theme/tree.fx.css +++ b/src/.theme/tree.fx.css @@ -6,6 +6,11 @@ -fx-background-color: -base-color; } +.tree-view { + -fx-border-width: 0 1 0 0; + -fx-border-color: derive(-second-base-color, -10%); +} + .tree-cell { -fx-background-color: -base-color; -fx-text-fill: -text-color; diff --git a/src/app/Dependency.php b/src/app/Dependency.php index 701a35e..2f5c53b 100644 --- a/src/app/Dependency.php +++ b/src/app/Dependency.php @@ -13,11 +13,13 @@ class Dependency const DEFAULT_DEPENDENCY_PATH = '\DevelNextLibrary\bundles\\'; const DEPENDENCY_ICON_PATH = '.data/img/develnext/bundle/'; + /** * @var ObjectStorage */ private $imageCache; + public function getDependencys (ZipFile $zip) { app()->form("MainForm")->flowPane->children->clear(); @@ -104,6 +106,7 @@ public function getDependencys (ZipFile $zip) { } } + private function getPanelWidth ($panel) { return $panel->children->offsetGet(1)->font->calculateTextWidth($panel->children->offsetGet(1)->text) + 16; } @@ -153,6 +156,7 @@ public function makeUi ($image, $name) { return $panel; } + private function getLink($name) { static $json = json_decode(FileStream::of('res://.data/dependencys.json'), true); @@ -183,6 +187,7 @@ private function getLink($name) { return $found[$name]["link"]; } + /** * Иконки стандартных пакетов DN * @@ -211,6 +216,7 @@ public function getIcon ($bundleName) { return new UXImage($name, 16, 16); } + /** * Иконки пользовательских пакетов, елси они были установленны в студии */ diff --git a/src/app/FSTreeProvider.php b/src/app/FSTreeProvider.php index 1a86653..70908e8 100644 --- a/src/app/FSTreeProvider.php +++ b/src/app/FSTreeProvider.php @@ -10,25 +10,41 @@ class FSTreeProvider implements IEvents { + const EMPTY_PATH_ELEMENT = '/path/'; + /** * @var Dependency */ private $dependency; + /** + * @var ObjectStorage + */ + private $imageCache; + /** * @var UXTreeItem */ private $rootItem; + /** + * @var TreeHelper + */ + public $treeHelper; + private $lastSelectedProject; private $selectedDirectory; + public function __construct (UXTreeItem $rootTreeItem) { $this->rootItem = $rootTreeItem; $this->dependency = new Dependency(); + $this->imageCache = new ObjectStorage(); + $this->treeHelper = new TreeHelper(); } + public function setDirectory ($path) { $this->selectedDirectory = $path; @@ -38,13 +54,16 @@ public function setDirectory ($path) { $items = explode('\\', $filePath); if ($file->isFile()) { - $this->createTreeItem($this->rootItem, $items); + $this->treeHelper->makeTree($this->rootItem, $items, function ($node, bool $isDir) use ($filePath) { + $this->applyIcon($node, ($isDir) ? FSTreeProvider::EMPTY_PATH_ELEMENT : $filePath); + }); } }]); - $this->sort($this->rootItem); + $this->treeHelper->sort($this->rootItem); } + /** * @return StandartFileSystem */ @@ -59,6 +78,7 @@ public function getFileByNode (UXTreeItem $item) { return false; } + /** * @return ZipFileSystem */ @@ -77,6 +97,7 @@ public function getZipByNode (UXTreeItem $item) { return false; } + public function getFileInfo (UXTreeItem $item) { $fs = new StandartFileSystem(); $filePath = $this->selectedDirectory . $fs->getAbsolutePath($item); @@ -85,16 +106,16 @@ public function getFileInfo (UXTreeItem $item) { if ($fs->isFile($filePath)) { if (!array_key_exists($filePath, $this->zipFiles)) { $this->zipFiles[$filePath] = new ZipFileSystem(); - + $zipFile = new ZipFile($filePath); - + $this->zipFiles[$filePath]->setZipInstance($zipFile); // отрисовываем структуру выбранной директории в дереве $this->createTreeItemOfZip($item, $zipFile->statAll()); // сортируем по алфавиту и чтобы сначала шли директории - $this->sort($item); + $this->treeHelper->sort($item); } call_user_func_array($this->events["onFileSystem"], [$fs, $filePath]); @@ -102,7 +123,6 @@ public function getFileInfo (UXTreeItem $item) { call_user_func_array($this->events["onFileSystem"], [$fs, $filePath]); } else { // если выбранный елемент является файлом в zip архиве list($fsPath, $zipPath) = $this->getPaths($filePath); - try { if (!($this->zipFiles[$fsPath] instanceof ZipFileSystem)) { $message = sprintf("Instance: %s ZipPath: %s FsPath: %s", get_class($this->zipFiles[$fsPath]) ?: "null", $zipPath, $fsPath); @@ -134,11 +154,13 @@ public function getFileInfo (UXTreeItem $item) { } } + public function onFileSystem (callable $callback) { if (!is_callable($callback)) return; $this->events["onFileSystem"] = $callback; } + public function onZipFileSystem (callable $callback) { if (!is_callable($callback)) return; $this->events["onZipFileSystem"] = $callback; @@ -170,40 +192,18 @@ public function getPaths ($filePath) { return [$fsPath, $zipPath]; } - protected function sort ($node) { - if ($node->children->isNotEmpty()) { - $notempty = []; - $empty = []; - - // грубая сортировка с разделением на пустые и с под элементами ноды - foreach ($node->children as $key => $children) { - if ($children->children->isNotEmpty()) { - $notempty[] = $children; - } else { - $empty[] = $children; - } - } - - uasort($notempty, function ($a, $b) { - return str::compare($a->value, $b->value); - }); - - uasort($empty, function ($a, $b) { - return str::compare($a->value, $b->value); - }); - - $node->children->clear(); - $node->children->addAll($notempty); - $node->children->addAll($empty); - - foreach ($node->children as $children) { - $this->sort($children); - } - } - } - protected function applyIcon ($item, $path) { - if ($path !== 'path') { + + if (!($item instanceof UXTreeItem || $item instanceof UXLabel)) { + throw new IllegalArgumentException('$item must be instance UXTreeItem or UXLabel'); + } + + if ($path !== FSTreeProvider::EMPTY_PATH_ELEMENT) { + if ($this->imageCache->exists(fs::ext($path))) { + $item->graphic = new UXImageView($this->imageCache->get(fs::ext($path))); + return; + } + switch (fs::ext($path)) { case 'png': case 'gif': @@ -217,61 +217,43 @@ protected function applyIcon ($item, $path) { $file = 'res://.data/img/ui/php-file-60.png'; break; case 'fxml': $file = 'res://.data/img/ui/fxml-file-24.png'; break; - + default: $file = 'res://.data/img/ui/file-60.png'; } } else { $file = 'res://.data/img/ui/folder-60.png'; - } - - if (!($item instanceof UXTreeItem || $item instanceof UXLabel)) { - throw new IllegalArgumentException('$item must be instance UXTreeItem or UXLabel'); + + if ($this->imageCache->exists(FSTreeProvider::EMPTY_PATH_ELEMENT)) { + $item->graphic = new UXImageView($this->imageCache->get(FSTreeProvider::EMPTY_PATH_ELEMENT)); + return; + } else { + $item->graphic = new UXImageView(new UXImage($file, 20, 20)); + $this->imageCache->set(FSTreeProvider::EMPTY_PATH_ELEMENT, $item->graphic->image); + return; + } } $item->graphic = new UXImageView(new UXImage($file, 20, 20)); + $this->imageCache->set(fs::ext($path), $item->graphic->image); } + protected function createTreeItemOfZip (UXTreeItem $root, $stat) { foreach (array_keys($stat) as $path) { - $path = str_replace('\\', '/', $path); - $path = explode('/', $path); - $this->createTreeItem($root, $path); - } - } - - protected function createTreeItem (UXTreeItem $root, $items) { - foreach ($items as $key => $value) { - $value = trim($value); - - if (empty($value)) continue; + if ($stat[$path]["crc"] == 0) continue; // fix bug with archive downloaded from githhub (draws wrong icons, all dirs was as file) - if ($root->children->count() > 0) { - foreach ($root->children->toArray() as $child) { - if ($child->value === $value) { - $this->createTreeItem($child, array_slice($items, 1)); - return; - } - } - } + $path = str_replace('\\', '/', $path); + $_path = explode('/', $path); - $root->children->add($root = new UXTreeItem($value)); - $this->applyIcon($root, "path"); + $this->treeHelper->makeTree($root, $_path, function ($node, bool $isDir) use ($path) { + $this->applyIcon($node, ($isDir) ? FSTreeProvider::EMPTY_PATH_ELEMENT : $path); + }); } - - - $this->applyIcon($root, $value); + } - public function backTrace(UXTreeItem $item, $path = null) { - - if ($item->parent instanceof UXTreeItem) { - if (fs::name($this->path) === $item->parent->value) { - return $path . $item->parent->value; - } - - return $this->backTrace($item->parent, $path) . '\\' . $item->value; - } - - return $path; - } + + + + } \ No newline at end of file diff --git a/src/app/events/ContextMenuEvents.php b/src/app/events/ContextMenuEvents.php index e41dc25..6d42b5b 100644 --- a/src/app/events/ContextMenuEvents.php +++ b/src/app/events/ContextMenuEvents.php @@ -123,7 +123,7 @@ public function showInExplorer (UXEvent $event = null) { } private function getPath () { - $path = $this->form->fsTree->backTrace($this->form->tree->focusedItem); + $path = $this->form->fsTree->treeHelper->getPath($this->form->tree->focusedItem); return $this->form->fsTree->getPaths($path); } } \ No newline at end of file diff --git a/src/app/events/MainMenuEvents.php b/src/app/events/MainMenuEvents.php index 62599ad..4d8dd84 100644 --- a/src/app/events/MainMenuEvents.php +++ b/src/app/events/MainMenuEvents.php @@ -33,7 +33,7 @@ public function changeTheme ($ev, $menu, $themeList) { foreach ($menu->items as $menuItem) { $name = array_search($themeList, $menuItem->graphic->text, false); - if ($ev->sender === $menuItem->graphic) { + if ($ev->sender->graphic === $menuItem->graphic) { app()->form("MainForm")->ini->set('theme', $name); app()->form("MainForm")->data('theme', $name); @@ -44,6 +44,7 @@ public function changeTheme ($ev, $menu, $themeList) { } $menuItem->graphic->enabled = false; + $menuItem->graphic->selected = true; app()->form("MainForm")->addStylesheet('.theme/' . $name . '.theme.fx.css'); diff --git a/src/app/forms/MainForm.php b/src/app/forms/MainForm.php index 6751468..f2e7803 100644 --- a/src/app/forms/MainForm.php +++ b/src/app/forms/MainForm.php @@ -12,6 +12,7 @@ class MainForm extends AbstractForm const REGISTRY_PATH = 'HKCU\SOFTWARE\ProjectView'; + /** * @var FSTreeProvider */ @@ -39,16 +40,18 @@ class MainForm extends AbstractForm */ public $mainMenuEvents; + /** * @event construct */ function doConstruct(UXEvent $e = null) { - $this->reg = Registry::of(self::REGISTRY_PATH); $this->logger = new LoggerReporter(); $this->mainMenuEvents = new MainMenuEvents(); try { + $this->reg = Registry::of(self::REGISTRY_PATH); + if (($path = $this->reg->read('ProjectDirectory')) !== null) { $this->ini->set("ProjectDirectory", $path); $this->reg->clear(); // удаляем старые записи в реестре т.к. сохраняем настрйоки в ini теперь @@ -76,44 +79,30 @@ function doConstruct(UXEvent $e = null) $bar->classes->add("menu-bar"); $bar->leftAnchor = $bar->rightAnchor = 0; - $bar->menus->add($menu = new UXMenu()); - $menu->graphic = new UXLabel("Выбрать директорию"); - $menu->graphic->padding = 1; - $menu->graphic->on("click", function () { - $this->mainMenuEvents->selectedFolder(); - }); - $bar->menus->add($menu = new UXMenu()); - $menu->graphic = new UXLabel("Цветовая схема"); + ContextMenuHelper::of($bar)->addCategory("Выбрать директорию", [$this->mainMenuEvents, 'selectedFolder']); + $themeCategory = ContextMenuHelper::of($bar)->addCategory("Цветовая схема"); $themeList = ["light" => "Светлая", "dark" => "Темная", "nord" => "Nord"]; - foreach ($themeList as $theme => $text) { - $menu->items->add($node = new UXMenuItem()); - $node->graphic = new UXCheckbox($text); + $themeCategory->addItem(null, function ($ev) use ($themeCategory, $themeList) { + $this->mainMenuEvents->changeTheme($ev, $themeCategory->getTarget(), $themeList); + }, $node = new UXCheckbox($text)); if ($theme === $this->data('theme')) { - $node->graphic->selected = true; - $node->graphic->enabled = false; + $node->selected = true; + $node->enabled = false; $this->addStylesheet('.theme/' . $theme. '.theme.fx.css'); } - - $node->graphic->on("action", function ($ev) use ($menu, $themeList) { - $this->mainMenuEvents->changeTheme($ev, $menu, $themeList); - }); } - - - $bar->menus->add($menu = new UXMenu()); - $menu->graphic = new UXLabel("О программе"); - $menu->graphic->padding = 1; - $menu->graphic->on("click", function () { + ContextMenuHelper::of($bar)->addCategory("О программе", function () { $this->form("About")->showAndWait(); }); + $this->add($bar); $this->infoPanelSwitcher->toFront(); @@ -196,6 +185,7 @@ function doConstruct(UXEvent $e = null) $this->show(); } + /** * @event tree.click-Left */ @@ -223,8 +213,6 @@ function doTreeClickLeft(UXMouseEvent $e = null) } - - /** * @event browser.running */ @@ -255,6 +243,7 @@ function doBrowserRunning(UXEvent $e = null) } } + /** * @event tree.click-Right */ @@ -299,22 +288,26 @@ function doTreeClickRight(UXMouseEvent $e = null) $contextRoot->showByNode($e->sender, $e->x, $e->y); } + /** * @event infoPanelSwitcher.click-Left */ function doInfoPanelSwitcherClickLeft(UXMouseEvent $e = null) { + $padding = 8; + if ($this->infoPanelSwitcher->selected) { - $this->tabPane->rightAnchor = $this->fileInfo->width + 16; - $this->fileInfo->rightAnchor = 8; + $this->tabPane->rightAnchor = $this->fileInfo->width + $padding * 2; + $this->fileInfo->rightAnchor = $padding; $this->ini->set('panel_file_information_show', 1); } else { - $this->tabPane->rightAnchor = 8; - $this->fileInfo->rightAnchor -= $this->fileInfo->width + 8; + $this->tabPane->rightAnchor = $padding; + $this->fileInfo->rightAnchor -= $this->fileInfo->width + $padding; $this->ini->set('panel_file_information_show', 0); } } + /** * @event infoPanelSwitcher.construct @@ -328,6 +321,7 @@ function doInfoPanelSwitcherConstruct(UXEvent $e = null) } } catch (Exception $ignore) {} } + /** * @event fileInfo.construct @@ -336,6 +330,7 @@ function doFileInfoConstruct(UXEvent $e = null) { $e->sender->lookup('.panel-title')->topAnchor = -14; } + /** * @event browser.load @@ -345,6 +340,7 @@ function doBrowserLoad(UXEvent $e = null) $this->doBrowserRunning($e); } + /** * @event tabPane.construct */ @@ -373,12 +369,14 @@ public function getHighlightType ($zipPath) { return $ext; } + public function updateFileinfo ($provider, $path) { $this->createdAt->text = $provider->createdAt($path); $this->modifiedAt->text = $provider->modifiedAt($path); $this->showMeta(["size" => $provider->size($path)]); } + private function showCodeInBrowser ($output, $ext = 'config') { $output = str_replace(['<', '>'], ['<', '>'], $output); $this->browser->engine->loadContent( @@ -386,6 +384,7 @@ private function showCodeInBrowser ($output, $ext = 'config') { ); } + private function showMeta ($meta) { $meta = $meta["size"]; $types = ['b', 'kb', 'mb', 'gb']; @@ -394,40 +393,4 @@ private function showMeta ($meta) { $this->fileSize->text = round($meta / pow(1024, $index), 2) . $types[$index]; } - - - public function errorAlert (Exception $ex, $detailed = false) { - $this->logger->discord($ex->getTraceAsString(), LoggerReporter::ERROR)->send(); - - $alert = new UXAlert("ERROR"); - $alert->headerText = ""; - - if ($detailed) { - $alert->expanded = false; - $alert->expandableContent = new UXScrollPane(new UXAnchorPane); - $alert->expandableContent->height = 400; - $alert->expandableContent->content->add(new UXLabel(var_export($ex->getTraceAsString(), true))); - } - - $alert->title = 'Произошла ошибка'; - $alert->contentText = $ex->getMessage(); - $alert->show(); - } - - public function _showForm ($formData, $outputImage) { - $n = new Environment(); - $n->importAutoLoaders(); - - // несовсем понимаю почему требуется импорт именно класса MainForm, причем не важно какое имя у загружаемой формы - $n->importClass(MainForm::class); - $n->importClass('php\gui\framework\AbstractForm'); - $n->importClass('php\gui\framework\AbstractModule'); - $n->execute(function () use ($formData, $outputImage) { - $layout = new UXLoader()->loadFromString($formData); - $form = new UXForm(); - $form->add($layout); - $outputImage->image = $form->layout->snapshot(); - }); - } - } diff --git a/src/app/helpers/ContextMenuHelper.php b/src/app/helpers/ContextMenuHelper.php index 13570ea..f04796d 100644 --- a/src/app/helpers/ContextMenuHelper.php +++ b/src/app/helpers/ContextMenuHelper.php @@ -24,8 +24,8 @@ class ContextMenuHelper * @return ContextMenuHelper */ public static function of ($target, Configuration $config = null) { - if (!($target instanceof UXContextMenu || $target instanceof UXMenu)) { - throw new IllegalArgumentException('Args most be php\gui\UXContextMenu or php\gui\UXMenu, now $target = ' . get_class($target)); + if (!($target instanceof UXContextMenu || $target instanceof UXMenu || $target instanceof UXMenuBar)) { + throw new IllegalArgumentException('Args most be php\gui\UXContextMenu or php\gui\UXMenu ot php\gui\UXMenuBar, now $target = ' . get_class($target)); } return new self($target, $config); @@ -50,13 +50,26 @@ public function addItem ($text, $callback, UXNode $graphic = null) { /** * @return ContextMenuHelper */ - public function addCategory ($text, UXNode $graphic = null) { - $this->target->items->add($node = new UXMenu($text)); - $this->setGraphic($node, $graphic); + public function addCategory ($text, callable $callback = null, UXNode $graphic = null) { + $param = 'items'; + + if ($this->target instanceof UXMenuBar) { + $param = 'menus'; + } + $this->target->{$param}->add($node = new UXMenu()); + $this->setGraphic($node, $label = new UXLabel($text)); + + if ($callback !== null) { + $label->on("click", $callback); + } return ContextMenuHelper::of($node, $this->config); } + public function getTarget () { + return $this->target; + } + public function addSeparator () { $this->target->items->add(UXMenu::createSeparator()); } diff --git a/src/app/helpers/TreeHelper.php b/src/app/helpers/TreeHelper.php new file mode 100644 index 0000000..39d6a28 --- /dev/null +++ b/src/app/helpers/TreeHelper.php @@ -0,0 +1,88 @@ + $value) { + $value = trim($value); + + if (empty($value)) continue; + + if ($root->children->count() > 0) { + foreach ($root->children->toArray() as $child) { + if ($child->value === $value) { + $this->makeTree($child, array_slice($items, 1), $callback); + return; + } + } + } + + $root->children->add($root = new UXTreeItem($value)); + + + if (is_callable($callback)) { + $callback($root, true); + } + } + + if (is_callable($callback)) { + $callback($root, false); + } + } + + /** + * @param UXTreeItem $node + * @param string $path + * @return string + */ + function getPath(UXTreeItem $node, $path = ""): string + { + if ($node->parent instanceof UXTreeItem) { + $path = $node->value . "\\" . $path; + + return $this->getPath($node->parent, $path); + } + + return rtrim($node->value . "\\" . $path, "\\"); + } + + /** + * @param UXTreeItem $node + */ + public function sort(UXTreeItem $node): void + { + if ($node->children->isNotEmpty()) { + $notEmpty = []; + $empty = []; + + foreach ($node->children as $key => $children) { + if ($children->children->isNotEmpty()) { + $notEmpty[] = $children; + } else { + $empty[] = $children; + } + } + + uasort($notEmpty, function ($a, $b) { + return str::compare($a->value, $b->value); + }); + + uasort($empty, function ($a, $b) { + return str::compare($a->value, $b->value); + }); + + $node->children->clear(); + $node->children->addAll($notEmpty); + $node->children->addAll($empty); + + foreach ($node->children as $children) { + $this->sort($children); + } + } + } +} \ No newline at end of file diff --git a/src/app/modules/AppModule.php b/src/app/modules/AppModule.php index 58a13f8..bfab900 100644 --- a/src/app/modules/AppModule.php +++ b/src/app/modules/AppModule.php @@ -9,7 +9,7 @@ class AppModule extends AbstractModule { const SELF_UPDATE_DELAY = 10000; - const APP_VERSION = '1.1.5'; + const APP_VERSION = '1.1.6'; const APP_TITLE = 'DevelNext ProjectView'; const UPDATE_TEMP_PATH = '\AppData\Local\Temp'; @@ -96,7 +96,12 @@ public function update () { Logger::info('Checking updates...'); - $selfUpdate = new Selfupdate('silentdeath76', 'DevelNext-ProjectViewer'); + try { + $selfUpdate = new Selfupdate('silentdeath76', 'DevelNext-ProjectViewer'); + } catch (Exception $ex) { + Logger::error($ex->getMessage()); + return; + } try { $response = $selfUpdate->getLatest(); diff --git a/src/app/modules/MainModule.php b/src/app/modules/MainModule.php index 89e3ca3..986835f 100644 --- a/src/app/modules/MainModule.php +++ b/src/app/modules/MainModule.php @@ -1,6 +1,7 @@ logger->discord($ex->getTraceAsString(), LoggerReporter::ERROR)->send(); + + $alert = new UXAlert("ERROR"); + $alert->headerText = ""; + + if ($detailed) { + $alert->expanded = false; + $alert->expandableContent = new UXScrollPane(new UXAnchorPane); + $alert->expandableContent->height = 400; + $alert->expandableContent->content->add(new UXLabel(var_export($ex->getTraceAsString(), true))); + } + + $alert->title = 'Произошла ошибка'; + $alert->contentText = $ex->getMessage(); + $alert->show(); + } + + + public function _showForm ($formData, $outputImage) { + $n = new Environment(); + $n->importAutoLoaders(); + + $n->importClass(MainForm::class); + $n->importClass(MainModule::class); + + $n->execute(function () use ($formData, $outputImage) { + $layout = new UXLoader()->loadFromString($formData); + $form = new UXForm(); + $form->add($layout); + $outputImage->image = $form->layout->snapshot(); + }); + } public function firstRunReport () { if ($this->ini->get("firstRun") != 1) { diff --git a/src/app/util/Selfupdate.php b/src/app/util/Selfupdate.php index 584db3d..fcba7db 100644 --- a/src/app/util/Selfupdate.php +++ b/src/app/util/Selfupdate.php @@ -1,6 +1,7 @@ owner, $this->repos], $this->url); - $array = json_decode($this->http->get($url)->body(), true); + $http = $this->http->get($url); + + if ($http->statusCode() !== 200) { + return Releases::of(["error" => "cant connected to github.com"]); + } + + $array = json_decode($http->body(), true); return Releases::of($array); } diff --git a/src/app/util/dto/Releases.php b/src/app/util/dto/Releases.php index 98b70f2..e9f69e9 100644 --- a/src/app/util/dto/Releases.php +++ b/src/app/util/dto/Releases.php @@ -17,6 +17,10 @@ public static function of ($obj) { throw new IllegalArgumentException("Argument must be array"); } + if (isset($obj["error"])) { + throw new RuntimeException($obj["error"]); + } + if (!isset($obj["tag_name"]) || !isset($obj["assets"])) { throw new RuntimeException("Broken array object"); }