diff --git a/src/gui/FileListWidget.cxx b/src/gui/FileListWidget.cxx index 65b7d6972..429b7c29e 100644 --- a/src/gui/FileListWidget.cxx +++ b/src/gui/FileListWidget.cxx @@ -176,6 +176,14 @@ void FileListWidget::selectDirectory() setLocation(selected(), _selectedFile); } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void FileListWidget::selectDirectory(const FilesystemNode& node) +{ + if(node.getPath() != _node.getPath()) + addHistory(node); + setLocation(node, _selectedFile); +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void FileListWidget::selectParent() { diff --git a/src/gui/FileListWidget.hxx b/src/gui/FileListWidget.hxx index 81b8dc3ca..a71449b76 100644 --- a/src/gui/FileListWidget.hxx +++ b/src/gui/FileListWidget.hxx @@ -85,7 +85,9 @@ class FileListWidget : public StringListWidget const string& select = EmptyString); /** Descend into currently selected directory */ - virtual void selectDirectory(); + void selectDirectory(); + /** Go to directory */ + void selectDirectory(const FilesystemNode& node); /** Select parent directory (if applicable) */ void selectParent(); /** Check if the there is a previous directory in history */ diff --git a/src/gui/LauncherDialog.cxx b/src/gui/LauncherDialog.cxx index d71f290ab..9e6a70ae0 100644 --- a/src/gui/LauncherDialog.cxx +++ b/src/gui/LauncherDialog.cxx @@ -71,15 +71,14 @@ LauncherDialog::LauncherDialog(OSystem& osystem, DialogContainer& parent, { const bool bottomButtons = instance().settings().getBool("launcherbuttons"); int ypos = Dialog::vBorder(); - WidgetArray wid; myUseMinimalUI = instance().settings().getBool("minimal_ui"); - addOptionWidgets(ypos, wid); - addPathWidgets(ypos, wid); - addRomWidgets(ypos, wid); + addOptionWidgets(ypos); + addPathWidgets(ypos); + addRomWidgets(ypos); if(!myUseMinimalUI && bottomButtons) - addButtonWidgets(ypos, wid); + addButtonWidgets(ypos); myNavigationBar->setList(myList); tooltip().setFont(_font); @@ -89,8 +88,6 @@ LauncherDialog::LauncherDialog(OSystem& osystem, DialogContainer& parent, else mySelectedItem = 9; // skip filter items and 5 navigation buttons - addToFocusList(wid); - // Do we show only ROMs or all files? toggleShowAll(false); @@ -99,7 +96,7 @@ LauncherDialog::LauncherDialog(OSystem& osystem, DialogContainer& parent, } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void LauncherDialog::addOptionWidgets(int& ypos, WidgetArray& wid) +void LauncherDialog::addOptionWidgets(int& ypos) { const int lineHeight = Dialog::lineHeight(), fontHeight = Dialog::fontHeight(), @@ -112,6 +109,7 @@ void LauncherDialog::addOptionWidgets(int& ypos, WidgetArray& wid) btnYOfs = (buttonHeight - lineHeight) / 2 + 1; string lblFound = "12345 items found"; int lwFound = _font.getStringWidth(lblFound); + WidgetArray wid; if(myUseMinimalUI) { @@ -200,11 +198,13 @@ void LauncherDialog::addOptionWidgets(int& ypos, WidgetArray& wid) lwFound, fontHeight, "", TextAlign::Right); ypos += lineHeight + VGAP * 2; + } + addToFocusList(wid); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void LauncherDialog::addPathWidgets(int& ypos, WidgetArray& wid) +void LauncherDialog::addPathWidgets(int& ypos) { // Add some buttons and textfield to show current directory const int @@ -222,8 +222,9 @@ void LauncherDialog::addPathWidgets(int& ypos, WidgetArray& wid) const int buttonWidth = iconWidth + ((fontWidth + 1) & ~0b1) - 1; // round up to next odd const int buttonHeight = lineHeight + 2; const int wNav = _w - HBORDER * 2 - (myUseMinimalUI ? lwFound + LBL_GAP : buttonWidth + BTN_GAP); - int xpos = HBORDER; + WidgetArray wid; + myNavigationBar = new NavigationWidget(this, _font, xpos, ypos, wNav, buttonHeight); if(!myUseMinimalUI) @@ -246,10 +247,11 @@ void LauncherDialog::addPathWidgets(int& ypos, WidgetArray& wid) lwFound + LBL_GAP + 1, lineHeight, ""); e->setEditable(false, true); } + addToFocusList(wid); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void LauncherDialog::addRomWidgets(int& ypos, WidgetArray& wid) +void LauncherDialog::addRomWidgets(int& ypos) { const bool bottomButtons = instance().settings().getBool("launcherbuttons"); const int lineHeight = Dialog::lineHeight(), @@ -261,6 +263,7 @@ void LauncherDialog::addRomWidgets(int& ypos, WidgetArray& wid) ? -VGAP * 4 : bottomButtons ? Dialog::buttonHeight() : -VGAP * 2; int xpos = HBORDER; + WidgetArray wid; // Add list with game titles // Before we add the list, we need to know the size of the RomInfoWidget @@ -297,10 +300,11 @@ void LauncherDialog::addRomWidgets(int& ypos, WidgetArray& wid) myRomInfoWidget = new RomInfoWidget(this, *myROMInfoFont, xpos, ypos, romWidth, myList->getHeight(), imgSize); } + addToFocusList(wid); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void LauncherDialog::addButtonWidgets(int& ypos, WidgetArray& wid) +void LauncherDialog::addButtonWidgets(int& ypos) { const bool bottomButtons = instance().settings().getBool("launcherbuttons"); const int lineHeight = Dialog::lineHeight(), @@ -314,6 +318,7 @@ void LauncherDialog::addButtonWidgets(int& ypos, WidgetArray& wid) : bottomButtons ? Dialog::buttonHeight() : -VGAP * 2, buttonWidth = (_w - 2 * HBORDER - BUTTON_GAP * (4 - 1)); int xpos = HBORDER; + WidgetArray wid; // Add four buttons at the bottom ypos = _h - VBORDER - buttonHeight; @@ -357,6 +362,7 @@ void LauncherDialog::addButtonWidgets(int& ypos, WidgetArray& wid) wid.push_back(myStartButton); #endif myStartButton->setToolTip("Start emulation of selected ROM\nor switch to selected directory."); + addToFocusList(wid); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/gui/LauncherDialog.hxx b/src/gui/LauncherDialog.hxx index ef085c9a0..78fc6ed0f 100644 --- a/src/gui/LauncherDialog.hxx +++ b/src/gui/LauncherDialog.hxx @@ -122,10 +122,10 @@ class LauncherDialog : public Dialog, CommandSender void loadConfig() override; void saveConfig() override; void updateUI(); - void addOptionWidgets(int& ypos, WidgetArray& wid); - void addPathWidgets(int& ypos, WidgetArray& wid); - void addRomWidgets(int& ypos, WidgetArray& wid); - void addButtonWidgets(int& ypos, WidgetArray& wid); + void addOptionWidgets(int& ypos); + void addPathWidgets(int& ypos); + void addRomWidgets(int& ypos); + void addButtonWidgets(int& ypos); string getRomDir(); /** diff --git a/src/gui/NavigationWidget.cxx b/src/gui/NavigationWidget.cxx index 890263411..fd58435bd 100644 --- a/src/gui/NavigationWidget.cxx +++ b/src/gui/NavigationWidget.cxx @@ -18,6 +18,7 @@ #include "Command.hxx" #include "Dialog.hxx" #include "EditTextWidget.hxx" +#include "FBSurface.hxx" #include "FileListWidget.hxx" #include "Icons.hxx" #include "OSystem.hxx" @@ -31,9 +32,9 @@ NavigationWidget::NavigationWidget(GuiObject* boss, const GUI::Font& font, { // Add some buttons and textfield to show current directory const int lineHeight = _font.getLineHeight(); - const bool useMinimalUI = instance().settings().getBool("minimal_ui"); + myUseMinimalUI = instance().settings().getBool("minimal_ui"); - if(!useMinimalUI) + if(!myUseMinimalUI) { const int fontHeight = _font.getFontHeight(), @@ -71,10 +72,15 @@ NavigationWidget::NavigationWidget(GuiObject* boss, const GUI::Font& font, myUpButton->setToolTip("Go Up"); boss->addFocusWidget(myUpButton); xpos = myUpButton->getRight() + BTN_GAP; + + myPath = new PathWidget(boss, this, _font, xpos, ypos, _w + _x - xpos, lineHeight); + } + else + { + myDir = new EditTextWidget(boss, _font, xpos, ypos, _w + _x - xpos, lineHeight, ""); + myDir->setEditable(false, true); + myDir->clearFlags(Widget::FLAG_RETAIN_FOCUS); } - myDir = new EditTextWidget(boss, _font, xpos, ypos, _w + _x - xpos, lineHeight, ""); - myDir->setEditable(false, true); - myDir->clearFlags(Widget::FLAG_RETAIN_FOCUS); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -83,21 +89,23 @@ void NavigationWidget::setList(FileListWidget* list) myList = list; // Let the FileListWidget handle the button commands - if(myHomeButton) + if(!myUseMinimalUI) + { myHomeButton->setTarget(myList); - if(myPrevButton) myPrevButton->setTarget(myList); - if(myNextButton) myNextButton->setTarget(myList); - if(myUpButton) myUpButton->setTarget(myList); + } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void NavigationWidget::setWidth(int w) { // Adjust path display accordingly too - myDir->setWidth(w - (myDir->getLeft() - _x)); + if(myUseMinimalUI) + myDir->setWidth(w - (myDir->getLeft() - _x)); + else + myPath->setWidth(w - (myPath->getLeft() - _x)); Widget::setWidth(w); } @@ -105,15 +113,147 @@ void NavigationWidget::setWidth(int w) void NavigationWidget::updateUI() { // Only enable the navigation buttons if function is available - if(myHomeButton) + if(myUseMinimalUI) + { + myDir->setText(myList->currentDir().getShortPath()); + } + else + { myHomeButton->setEnabled(myList->hasPrevHistory()); - if(myPrevButton) myPrevButton->setEnabled(myList->hasPrevHistory()); - if(myNextButton) myNextButton->setEnabled(myList->hasNextHistory()); - if(myUpButton) myUpButton->setEnabled(myList->currentDir().hasParent()); - - // Show current directory - myDir->setText(myList->currentDir().getShortPath()); + myPath->setPath(myList->currentDir().getShortPath()); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void NavigationWidget::handleCommand(CommandSender* sender, int cmd, int data, int id) +{ + switch(cmd) + { + case kFolderClicked: + { + FilesystemNode node(myPath->getPath(id)); + myList->selectDirectory(node); + break; + } + + default: + break; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +NavigationWidget::PathWidget::PathWidget(GuiObject* boss, CommandReceiver* target, + const GUI::Font& font, int xpos, int ypos, int w, int h) + : Widget(boss, font, xpos, ypos, w, h), + myTarget{target} +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void NavigationWidget::PathWidget::setPath(const string& path) +{ + const int lineHeight = _font.getLineHeight(); + const int fontWidth = _font.getMaxCharWidth(); + int x = _x + fontWidth, w = _w; + FilesystemNode node(path); + + // Calculate how many path parts can be displayed + StringList paths; + bool cutFirst = false; + while(node.hasParent() && w >= fontWidth * 1) + { + const string& name = node.getName(); + int l = int(name.length() + 2); + + if(name.back() == FilesystemNode::PATH_SEPARATOR) + l--; + if(node.getParent().hasParent()) + l++; + + w -= l * fontWidth; + paths.push_back(node.getPath()); + node = node.getParent(); + } + if(w < 0 || node.hasParent()) + cutFirst = true; + + // Update/add widgets for path parts display + int idx = 0; + for(auto it = paths.rbegin(); it != paths.rend(); ++it, ++idx) + { + const string& curPath = *it; + node = FilesystemNode(curPath); + string name = node.getName(); + + if(it == paths.rbegin() && cutFirst) + name = ">"; + else + { + if(name.back() == FilesystemNode::PATH_SEPARATOR) + name.pop_back(); + if(it + 1 != paths.rend()) + name += " >"; + } + const int width = int(name.length() + 1) * fontWidth; + + if(myFolderList.size() > idx) + { + myFolderList[idx]->setPath(curPath); + myFolderList[idx]->setPosX(x); + myFolderList[idx]->setWidth(width); + myFolderList[idx]->setLabel(name); + } + else + { + // Add new widget to list + FolderLinkWidget* s = new FolderLinkWidget(_boss, _font, x, _y, + width, lineHeight + 2, name, curPath); + s->setID(idx); + s->setTarget(myTarget); + myFolderList.push_back(s); + _boss->addFocusWidget(s); + } + x += width; + } + // Hide any remaining widgets + while(idx < size(myFolderList)) + { + myFolderList[idx]->setWidth(0); + ++idx; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const string& NavigationWidget::PathWidget::getPath(int idx) const +{ + assert(idx < myFolderList.size()); + return myFolderList[idx]->getPath(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +NavigationWidget::PathWidget::FolderLinkWidget::FolderLinkWidget( + GuiObject* boss, const GUI::Font& font, + int x, int y, int w, int h, const string& text, const string& path) + : ButtonWidget(boss, font, x, y, w, h, text, kFolderClicked), + myPath{path} +{ + _flags = Widget::FLAG_ENABLED | Widget::FLAG_CLEARBG; + + _bgcolor = kDlgColor; + _bgcolorhi = kBtnColorHi; + _textcolor = kTextColor; + _align = TextAlign::Center; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void NavigationWidget::PathWidget::FolderLinkWidget::drawWidget(bool hilite) +{ + FBSurface& s = _boss->dialog().surface(); + + if(hilite) + s.frameRect(_x, _y, _w, _h, kBtnBorderColorHi); + s.drawString(_font, _label, _x + 1, _y + 2, _w, hilite ? _textcolorhi : _textcolor, _align); } diff --git a/src/gui/NavigationWidget.hxx b/src/gui/NavigationWidget.hxx index a14fc20ac..b08b53899 100644 --- a/src/gui/NavigationWidget.hxx +++ b/src/gui/NavigationWidget.hxx @@ -26,6 +26,69 @@ class Font; class NavigationWidget : public Widget { + public: + enum { + kFolderClicked = 'flcl' + }; + + private: + class PathWidget : public Widget + { + private: + class FolderLinkWidget : public ButtonWidget + { + public: + FolderLinkWidget(GuiObject* boss, const GUI::Font& font, + int x, int y, int w, int h, const string& text, const string& path); + ~FolderLinkWidget() = default; + + void setPath(const string& path) { myPath = path; } + const string& getPath() const { return myPath; }; + + private: + void drawWidget(bool hilite) override; + + private: + string myPath; + + private: + // Following constructors and assignment operators not supported + FolderLinkWidget() = delete; + FolderLinkWidget(const FolderLinkWidget&) = delete; + FolderLinkWidget(FolderLinkWidget&&) = delete; + FolderLinkWidget& operator=(const FolderLinkWidget&) = delete; + FolderLinkWidget& operator=(FolderLinkWidget&&) = delete; + }; // FolderLinkWidget + + public: + PathWidget(GuiObject* boss, CommandReceiver* target, + const GUI::Font& font, int x, int y, int w, int h); + ~PathWidget() = default; + + void setPath(const string& path); + const string& getPath(int idx) const; + + private: + struct PathType + { + string path; + StaticTextWidget* widget{nullptr}; + + explicit PathType(const string& _path, StaticTextWidget* _widget) + : path{_path}, widget{_widget} {} + }; + std::vector myFolderList; + CommandReceiver* myTarget; + + private: + // Following constructors and assignment operators not supported + PathWidget() = delete; + PathWidget(const PathWidget&) = delete; + PathWidget(PathWidget&&) = delete; + PathWidget& operator=(const PathWidget&) = delete; + PathWidget& operator=(PathWidget&&) = delete; + }; // PathWidget + public: NavigationWidget(GuiObject* boss, const GUI::Font& font, int x, int y, int w, int h); @@ -36,13 +99,27 @@ class NavigationWidget : public Widget void updateUI(); private: + void handleCommand(CommandSender* sender, int cmd, int data, int id) override; + + private: + bool myUseMinimalUI{false}; + ButtonWidget* myHomeButton{nullptr}; ButtonWidget* myPrevButton{nullptr}; ButtonWidget* myNextButton{nullptr}; ButtonWidget* myUpButton{nullptr}; EditTextWidget* myDir{nullptr}; + PathWidget* myPath{nullptr}; FileListWidget* myList{nullptr}; -}; + + private: + // Following constructors and assignment operators not supported + NavigationWidget() = delete; + NavigationWidget(const NavigationWidget&) = delete; + NavigationWidget(NavigationWidget&&) = delete; + NavigationWidget& operator=(const NavigationWidget&) = delete; + NavigationWidget& operator=(NavigationWidget&&) = delete; +}; // NavigationWidget #endif \ No newline at end of file