enhanced folder navigation in launcher

This commit is contained in:
Thomas Jentzsch 2021-12-14 22:31:09 +01:00
parent e0048a7421
commit bd15d76643
6 changed files with 268 additions and 35 deletions

View File

@ -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()
{

View File

@ -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 */

View File

@ -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);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -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();
/**

View File

@ -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);
}

View File

@ -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<FolderLinkWidget*> 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