mirror of https://github.com/stella-emu/stella.git
enhanced folder navigation in launcher
This commit is contained in:
parent
e0048a7421
commit
bd15d76643
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
|
|
@ -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();
|
||||
|
||||
/**
|
||||
|
|
|
@ -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,11 +72,16 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void NavigationWidget::setList(FileListWidget* list)
|
||||
|
@ -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
|
||||
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)
|
||||
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
|
||||
if(myUseMinimalUI)
|
||||
{
|
||||
myDir->setText(myList->currentDir().getShortPath());
|
||||
}
|
||||
else
|
||||
{
|
||||
myHomeButton->setEnabled(myList->hasPrevHistory());
|
||||
myPrevButton->setEnabled(myList->hasPrevHistory());
|
||||
myNextButton->setEnabled(myList->hasNextHistory());
|
||||
myUpButton->setEnabled(myList->currentDir().hasParent());
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue