mirror of https://github.com/stella-emu/stella.git
added subdirectory search to launcher
enhanced ProgressDialog
This commit is contained in:
parent
4c97ec89c9
commit
1219fe0d2c
|
@ -76,9 +76,62 @@ bool FilesystemNode::exists() const
|
|||
return _realNode ? _realNode->exists() : false;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool FilesystemNode::getAllChildren(FSList& fslist, ListMode mode,
|
||||
const NameFilter& filter,
|
||||
bool includeParentDirectory) const
|
||||
{
|
||||
if(getChildren(fslist, mode, filter, includeParentDirectory))
|
||||
{
|
||||
// Sort only once at the end
|
||||
#if defined(ZIP_SUPPORT)
|
||||
// before sorting, replace single file ZIP archive names with contained file names
|
||||
// because they are displayed using their contained file names
|
||||
for(auto& i : fslist)
|
||||
{
|
||||
if(BSPF::endsWithIgnoreCase(i.getPath(), ".zip"))
|
||||
{
|
||||
FilesystemNodeZIP zipNode(i.getPath());
|
||||
|
||||
i.setName(zipNode.getName());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
std::sort(fslist.begin(), fslist.end(),
|
||||
[](const FilesystemNode& node1, const FilesystemNode& node2)
|
||||
{
|
||||
if(node1.isDirectory() != node2.isDirectory())
|
||||
return node1.isDirectory();
|
||||
else
|
||||
return BSPF::compareIgnoreCase(node1.getName(), node2.getName()) < 0;
|
||||
}
|
||||
);
|
||||
|
||||
#if defined(ZIP_SUPPORT)
|
||||
// After sorting replace zip files with zip nodes
|
||||
for(auto& i : fslist)
|
||||
{
|
||||
if(BSPF::endsWithIgnoreCase(i.getPath(), ".zip"))
|
||||
{
|
||||
// Force ZIP c'tor to be called
|
||||
AbstractFSNodePtr ptr = FilesystemNodeFactory::create(i.getPath(),
|
||||
FilesystemNodeFactory::Type::ZIP);
|
||||
FilesystemNode zipNode(ptr);
|
||||
i = zipNode;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool FilesystemNode::getChildren(FSList& fslist, ListMode mode,
|
||||
const NameFilter& filter,
|
||||
bool includeChildDirectories,
|
||||
bool includeParentDirectory) const
|
||||
{
|
||||
if (!_realNode || !_realNode->isDirectory())
|
||||
|
@ -90,12 +143,15 @@ bool FilesystemNode::getChildren(FSList& fslist, ListMode mode,
|
|||
if (!_realNode->getChildren(tmp, mode))
|
||||
return false;
|
||||
|
||||
// when incuding child directories, everything must be sorted once at the end
|
||||
if(!includeChildDirectories)
|
||||
{
|
||||
#if defined(ZIP_SUPPORT)
|
||||
// before sorting, replace single file ZIP archive names with contained file names
|
||||
// because they are displayed using their contained file names
|
||||
for (auto& i : tmp)
|
||||
for(auto& i : tmp)
|
||||
{
|
||||
if (BSPF::endsWithIgnoreCase(i->getPath(), ".zip"))
|
||||
if(BSPF::endsWithIgnoreCase(i->getPath(), ".zip"))
|
||||
{
|
||||
FilesystemNodeZIP node(i->getPath());
|
||||
|
||||
|
@ -104,15 +160,16 @@ bool FilesystemNode::getChildren(FSList& fslist, ListMode mode,
|
|||
}
|
||||
#endif
|
||||
|
||||
std::sort(tmp.begin(), tmp.end(),
|
||||
[](const AbstractFSNodePtr& node1, const AbstractFSNodePtr& node2)
|
||||
std::sort(tmp.begin(), tmp.end(),
|
||||
[](const AbstractFSNodePtr& node1, const AbstractFSNodePtr& node2)
|
||||
{
|
||||
if (node1->isDirectory() != node2->isDirectory())
|
||||
if(node1->isDirectory() != node2->isDirectory())
|
||||
return node1->isDirectory();
|
||||
else
|
||||
return BSPF::compareIgnoreCase(node1->getName(), node2->getName()) < 0;
|
||||
}
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
// Add parent node, if it is valid to do so
|
||||
if (includeParentDirectory && hasParent())
|
||||
|
@ -130,21 +187,44 @@ bool FilesystemNode::getChildren(FSList& fslist, ListMode mode,
|
|||
{
|
||||
// Force ZIP c'tor to be called
|
||||
AbstractFSNodePtr ptr = FilesystemNodeFactory::create(i->getPath(),
|
||||
FilesystemNodeFactory::Type::ZIP);
|
||||
FilesystemNode node(ptr);
|
||||
if (filter(node))
|
||||
fslist.emplace_back(node);
|
||||
FilesystemNodeFactory::Type::ZIP);
|
||||
FilesystemNode zipNode(ptr);
|
||||
|
||||
if(filter(zipNode))
|
||||
{
|
||||
if(!includeChildDirectories)
|
||||
fslist.emplace_back(zipNode);
|
||||
else
|
||||
{
|
||||
// filter by zip node but add file node
|
||||
FilesystemNode node(i);
|
||||
fslist.emplace_back(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
// Make directories stand out
|
||||
if (i->isDirectory())
|
||||
if(i->isDirectory())
|
||||
i->setName(" [" + i->getName() + "]");
|
||||
|
||||
FilesystemNode node(i);
|
||||
if (filter(node))
|
||||
fslist.emplace_back(node);
|
||||
|
||||
if(includeChildDirectories)
|
||||
{
|
||||
if(i->isDirectory())
|
||||
node.getChildren(fslist, mode, filter, includeChildDirectories, false);
|
||||
else
|
||||
// do not add directories in this mode
|
||||
if(filter(node))
|
||||
fslist.emplace_back(node);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(filter(node))
|
||||
fslist.emplace_back(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -114,6 +114,17 @@ class FilesystemNode
|
|||
*/
|
||||
bool exists() const;
|
||||
|
||||
/**
|
||||
* Return a list of child nodes of this and all sub-directories. If called on a node
|
||||
* that does not represent a directory, false is returned.
|
||||
*
|
||||
* @return true if successful, false otherwise (e.g. when the directory
|
||||
* does not exist).
|
||||
*/
|
||||
bool getAllChildren(FSList& fslist, ListMode mode = ListMode::DirectoriesOnly,
|
||||
const NameFilter& filter = [](const FilesystemNode&) { return true; },
|
||||
bool includeParentDirectory = true) const;
|
||||
|
||||
/**
|
||||
* Return a list of child nodes of this directory node. If called on a node
|
||||
* that does not represent a directory, false is returned.
|
||||
|
@ -123,6 +134,7 @@ class FilesystemNode
|
|||
*/
|
||||
bool getChildren(FSList& fslist, ListMode mode = ListMode::DirectoriesOnly,
|
||||
const NameFilter& filter = [](const FilesystemNode&){ return true; },
|
||||
bool includeChildDirectories = false,
|
||||
bool includeParentDirectory = true) const;
|
||||
|
||||
/**
|
||||
|
@ -273,8 +285,8 @@ class FilesystemNode
|
|||
string getPathWithExt(const string& ext) const;
|
||||
|
||||
private:
|
||||
AbstractFSNodePtr _realNode;
|
||||
explicit FilesystemNode(const AbstractFSNodePtr& realNode);
|
||||
AbstractFSNodePtr _realNode;
|
||||
void setPath(const string& path);
|
||||
};
|
||||
|
||||
|
|
|
@ -45,6 +45,8 @@ class ContextMenu : public Dialog, public CommandSender
|
|||
const VariantList& items, int cmd = 0, int width = 0);
|
||||
~ContextMenu() override = default;
|
||||
|
||||
bool isShading() const override { return false; }
|
||||
|
||||
/** Set the parent widget's ID */
|
||||
void setID(uInt32 id) { _id = id; }
|
||||
|
||||
|
|
|
@ -255,12 +255,11 @@ void Dialog::render()
|
|||
});
|
||||
}
|
||||
|
||||
// Dialog is still on top if e.g a dialog without title is opened
|
||||
// (e.g. ContextMenu)
|
||||
// A dialog is still on top if a non-shading dialog (e.g. ContextMenu)
|
||||
// is opended above it.
|
||||
bool onTop = parent().myDialogStack.top() == this
|
||||
|| (parent().myDialogStack.get(parent().myDialogStack.size() - 2) == this
|
||||
&& !parent().myDialogStack.top()->hasTitle());
|
||||
//&& typeid(*parent().myDialogStack.top()) == typeid(ContextMenu))
|
||||
&& !parent().myDialogStack.top()->isShading());
|
||||
|
||||
if(!onTop)
|
||||
{
|
||||
|
|
|
@ -94,6 +94,8 @@ class Dialog : public GuiObject
|
|||
void setTitle(const string& title);
|
||||
bool hasTitle() { return !_title.empty(); }
|
||||
|
||||
virtual bool isShading() const { return true; }
|
||||
|
||||
/**
|
||||
Determine the maximum width/height of a dialog based on the minimum
|
||||
allowable bounds, also taking into account the current window size.
|
||||
|
|
|
@ -171,7 +171,7 @@ void DialogContainer::removeDialog()
|
|||
{
|
||||
if(!myDialogStack.empty())
|
||||
{
|
||||
cerr << "remove dialog " << typeid(*myDialogStack.top()).name() << endl;
|
||||
//cerr << "remove dialog " << typeid(*myDialogStack.top()).name() << endl;
|
||||
myDialogStack.pop();
|
||||
|
||||
// Inform the frame buffer that it has to render all surfaces
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "ScrollBarWidget.hxx"
|
||||
#include "FileListWidget.hxx"
|
||||
#include "TimerManager.hxx"
|
||||
#include "ProgressDialog.hxx"
|
||||
|
||||
#include "bspf.hxx"
|
||||
|
||||
|
@ -72,22 +73,37 @@ void FileListWidget::setDirectory(const FilesystemNode& node,
|
|||
void FileListWidget::setLocation(const FilesystemNode& node,
|
||||
const string& select)
|
||||
{
|
||||
progress().resetProgress();
|
||||
progress().open();
|
||||
|
||||
_node = node;
|
||||
|
||||
// Read in the data from the file system (start with an empty list)
|
||||
_fileList.clear();
|
||||
_fileList.reserve(512);
|
||||
_node.getChildren(_fileList, _fsmode, _filter);
|
||||
|
||||
if(_includeSubDirs)
|
||||
{
|
||||
// Actually this could become HUGE
|
||||
_fileList.reserve(0x2000);
|
||||
_node.getAllChildren(_fileList, _fsmode, _filter);
|
||||
}
|
||||
else
|
||||
{
|
||||
_fileList.reserve(0x200);
|
||||
_node.getChildren(_fileList, _fsmode, _filter);
|
||||
}
|
||||
|
||||
// Now fill the list widget with the names from the file list
|
||||
StringList l;
|
||||
for(const auto& file: _fileList)
|
||||
for(const auto& file : _fileList)
|
||||
l.push_back(file.getName());
|
||||
|
||||
setList(l);
|
||||
setSelected(select);
|
||||
|
||||
ListWidget::recalc();
|
||||
|
||||
progress().close();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -114,6 +130,21 @@ void FileListWidget::reload()
|
|||
}
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
ProgressDialog& FileListWidget::progress()
|
||||
{
|
||||
if(myProgressDialog == nullptr)
|
||||
myProgressDialog = make_unique<ProgressDialog>(this, _font, "", false);
|
||||
|
||||
return *myProgressDialog;
|
||||
}
|
||||
|
||||
void FileListWidget::incProgress()
|
||||
{
|
||||
if(_includeSubDirs)
|
||||
progress().incProgress();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool FileListWidget::handleText(char text)
|
||||
{
|
||||
|
@ -202,3 +233,5 @@ void FileListWidget::handleCommand(CommandSender* sender, int cmd, int data, int
|
|||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
uInt64 FileListWidget::_QUICK_SELECT_DELAY = 300;
|
||||
|
||||
unique_ptr<ProgressDialog> FileListWidget::myProgressDialog{nullptr};
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#define FILE_LIST_WIDGET_HXX
|
||||
|
||||
class CommandSender;
|
||||
class ProgressDialog;
|
||||
|
||||
#include "FSNode.hxx"
|
||||
#include "Stack.hxx"
|
||||
|
@ -59,12 +60,16 @@ class FileListWidget : public StringListWidget
|
|||
_filter = filter;
|
||||
}
|
||||
|
||||
// When enabled, all subdirectories will be searched too.
|
||||
void setIncludeSubDirs(bool enable) { _includeSubDirs = enable; }
|
||||
|
||||
/**
|
||||
Set initial directory, and optionally select the given item.
|
||||
|
||||
@param node The directory to display. If this is a file, its parent
|
||||
will instead be used, and the file will be selected
|
||||
@param select An optional entry to select (if applicable)
|
||||
@param node The directory to display. If this is a file, its parent
|
||||
will instead be used, and the file will be selected
|
||||
@param select An optional entry to select (if applicable)
|
||||
@param recursive Recursively list sub-directories too
|
||||
*/
|
||||
void setDirectory(const FilesystemNode& node,
|
||||
const string& select = EmptyString);
|
||||
|
@ -84,6 +89,12 @@ class FileListWidget : public StringListWidget
|
|||
|
||||
static void setQuickSelectDelay(uInt64 time) { _QUICK_SELECT_DELAY = time; }
|
||||
|
||||
ProgressDialog& progress();
|
||||
void incProgress();
|
||||
|
||||
protected:
|
||||
static unique_ptr<ProgressDialog> myProgressDialog;
|
||||
|
||||
private:
|
||||
/** Very similar to setDirectory(), but also updates the history */
|
||||
void setLocation(const FilesystemNode& node, const string& select);
|
||||
|
@ -99,6 +110,7 @@ class FileListWidget : public StringListWidget
|
|||
FilesystemNode::NameFilter _filter;
|
||||
FilesystemNode _node;
|
||||
FSList _fileList;
|
||||
bool _includeSubDirs{false};
|
||||
|
||||
Common::FixedStack<string> _history;
|
||||
uInt32 _selected{0};
|
||||
|
|
|
@ -15,6 +15,9 @@
|
|||
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||
//============================================================================
|
||||
|
||||
// TODO:
|
||||
// - abort current file list reload when typing
|
||||
|
||||
#include "bspf.hxx"
|
||||
#include "Bankswitch.hxx"
|
||||
#include "BrowserDialog.hxx"
|
||||
|
@ -29,6 +32,7 @@
|
|||
#include "GlobalPropsDialog.hxx"
|
||||
#include "StellaSettingsDialog.hxx"
|
||||
#include "WhatsNewDialog.hxx"
|
||||
#include "ProgressDialog.hxx"
|
||||
#include "MessageBox.hxx"
|
||||
#include "ToolTip.hxx"
|
||||
#include "OSystem.hxx"
|
||||
|
@ -73,25 +77,71 @@ LauncherDialog::LauncherDialog(OSystem& osystem, DialogContainer& parent,
|
|||
buttonHeight = myUseMinimalUI ? lineHeight - VGAP * 2: lineHeight * 1.25,
|
||||
buttonWidth = (_w - 2 * HBORDER - BUTTON_GAP * (4 - 1));
|
||||
|
||||
int xpos = HBORDER, ypos = VBORDER, lwidth = 0, lwidth2 = 0;
|
||||
int xpos = HBORDER, ypos = VBORDER;
|
||||
WidgetArray wid;
|
||||
string lblRom = "Select a ROM from the list" + ELLIPSIS;
|
||||
string lblSelect = "Select a ROM from the list" + ELLIPSIS;
|
||||
string lblAllFiles = "Show all files";
|
||||
const string& lblFilter = "Filter";
|
||||
const string& lblAllFiles = "Show all files";
|
||||
const string& lblFound = "XXXX items found";
|
||||
string lblSubDirs = "Incl. subdirectories";
|
||||
string lblFound = "12345 items found";
|
||||
|
||||
tooltip().setFont(font);
|
||||
|
||||
lwidth = font.getStringWidth(lblRom);
|
||||
lwidth2 = font.getStringWidth(lblAllFiles) + CheckboxWidget::boxSize(font);
|
||||
int lwidth3 = font.getStringWidth(lblFilter);
|
||||
int lwidth4 = font.getStringWidth(lblFound);
|
||||
int lwSelect = font.getStringWidth(lblSelect);
|
||||
int cwAllFiles = font.getStringWidth(lblAllFiles) + CheckboxWidget::prefixSize(font);
|
||||
int lwFilter = font.getStringWidth(lblFilter);
|
||||
int cwSubDirs = font.getStringWidth(lblSubDirs) + CheckboxWidget::prefixSize(font);
|
||||
int lwFound = font.getStringWidth(lblFound);
|
||||
int wTotal = HBORDER * 2 + lwSelect + cwAllFiles + lwFilter + cwSubDirs + lwFound
|
||||
+ EditTextWidget::calcWidth(font, "123456") + LBL_GAP * 7;
|
||||
bool noSelect = false;
|
||||
|
||||
if(w < HBORDER * 2 + lwidth + lwidth2 + lwidth3 + lwidth4 + fontWidth * 6 + LBL_GAP * 8)
|
||||
if(w < wTotal)
|
||||
{
|
||||
// make sure there is space for at least 6 characters in the filter field
|
||||
lblRom = "Select a ROM" + ELLIPSIS;
|
||||
lwidth = font.getStringWidth(lblRom);
|
||||
lblSelect = "Select a ROM" + ELLIPSIS;
|
||||
int lwSelectShort = font.getStringWidth(lblSelect);
|
||||
|
||||
wTotal -= lwSelect - lwSelectShort;
|
||||
lwSelect = lwSelectShort;
|
||||
}
|
||||
if(w < wTotal)
|
||||
{
|
||||
// make sure there is space for at least 6 characters in the filter field
|
||||
lblSubDirs = "Subdir.";
|
||||
int cwSubDirsShort = font.getStringWidth(lblSubDirs) + CheckboxWidget::prefixSize(font);
|
||||
|
||||
wTotal -= cwSubDirs - cwSubDirsShort;
|
||||
cwSubDirs = cwSubDirsShort;
|
||||
}
|
||||
if(w < wTotal)
|
||||
{
|
||||
// make sure there is space for at least 6 characters in the filter field
|
||||
lblAllFiles = "All files";
|
||||
int cwAllFilesShort = font.getStringWidth(lblAllFiles) + CheckboxWidget::prefixSize(font);
|
||||
|
||||
wTotal -= cwAllFiles - cwAllFilesShort;
|
||||
cwAllFiles = cwAllFilesShort;
|
||||
}
|
||||
if(w < wTotal)
|
||||
{
|
||||
// make sure there is space for at least 6 characters in the filter field
|
||||
lblFound = "12345 found";
|
||||
int lwFoundShort = font.getStringWidth(lblFound);
|
||||
|
||||
wTotal -= lwFound - lwFoundShort;
|
||||
lwFound = lwFoundShort;
|
||||
myShortCount = true;
|
||||
}
|
||||
if(w < wTotal)
|
||||
{
|
||||
// make sure there is space for at least 6 characters in the filter field
|
||||
lblSelect = "";
|
||||
int lwSelectShort = font.getStringWidth(lblSelect);
|
||||
|
||||
wTotal -= lwSelect - lwSelectShort;
|
||||
lwSelect = lwSelectShort;
|
||||
noSelect = true;
|
||||
}
|
||||
|
||||
if(myUseMinimalUI)
|
||||
|
@ -108,31 +158,51 @@ LauncherDialog::LauncherDialog(OSystem& osystem, DialogContainer& parent,
|
|||
}
|
||||
|
||||
// Show the header
|
||||
new StaticTextWidget(this, font, xpos, ypos, lblRom);
|
||||
new StaticTextWidget(this, font, xpos, ypos, lblSelect);
|
||||
// Shop the files counter
|
||||
xpos = _w - HBORDER - lwidth4;
|
||||
xpos = _w - HBORDER - lwFound;
|
||||
myRomCount = new StaticTextWidget(this, font, xpos, ypos,
|
||||
lwidth4, fontHeight,
|
||||
lwFound, fontHeight,
|
||||
"", TextAlign::Right);
|
||||
|
||||
// Add filter that can narrow the results shown in the listing
|
||||
// It has to fit between both labels
|
||||
if(!myUseMinimalUI && w >= 640)
|
||||
{
|
||||
int fwidth = std::min(15 * fontWidth, xpos - lwidth3 - lwidth2 - lwidth - HBORDER - LBL_GAP * 8);
|
||||
int fwFilter = std::min(EditTextWidget::calcWidth(font, "123456789012345"),
|
||||
xpos - cwSubDirs - lwFilter - cwAllFiles
|
||||
- lwSelect - HBORDER - LBL_GAP * (noSelect ? 5 : 7));
|
||||
|
||||
// Show the subdirectories checkbox
|
||||
xpos -= cwSubDirs + LBL_GAP;
|
||||
mySubDirs = new CheckboxWidget(this, font, xpos, ypos, lblSubDirs, kSubDirsCmd);
|
||||
mySubDirs->setEnabled(false);
|
||||
ostringstream tip;
|
||||
tip << "Search files in subdirectories too.\n"
|
||||
<< "Filter must have at least " << MIN_SUBDIRS_CHARS << " chars.";
|
||||
mySubDirs->setToolTip(tip.str());
|
||||
|
||||
// Show the filter input field
|
||||
xpos -= fwidth + LBL_GAP;
|
||||
myPattern = new EditTextWidget(this, font, xpos, ypos - 2, fwidth, lineHeight, "");
|
||||
myPattern->setToolTip("Enter filter text to reduce file list.");
|
||||
xpos -= fwFilter + LBL_GAP;
|
||||
myPattern = new EditTextWidget(this, font, xpos, ypos - 2, fwFilter, lineHeight, "");
|
||||
myPattern->setToolTip("Enter filter text to reduce file list.\n"
|
||||
"Use '*' and '?' as wildcards.");
|
||||
|
||||
// Show the "Filter" label
|
||||
xpos -= lwidth3 + LBL_GAP;
|
||||
xpos -= lwFilter + LBL_GAP;
|
||||
new StaticTextWidget(this, font, xpos, ypos, lblFilter);
|
||||
|
||||
// Show the checkbox for all files
|
||||
xpos -= lwidth2 + LBL_GAP * 3;
|
||||
if(noSelect)
|
||||
xpos = HBORDER;
|
||||
else
|
||||
xpos -= cwAllFiles + LBL_GAP * 2;
|
||||
myAllFiles = new CheckboxWidget(this, font, xpos, ypos, lblAllFiles, kAllfilesCmd);
|
||||
myAllFiles->setToolTip("Uncheck to show ROM files only.");
|
||||
|
||||
wid.push_back(myAllFiles);
|
||||
wid.push_back(myPattern);
|
||||
wid.push_back(mySubDirs);
|
||||
}
|
||||
|
||||
// Add list with game titles
|
||||
|
@ -167,11 +237,11 @@ LauncherDialog::LauncherDialog(OSystem& osystem, DialogContainer& parent,
|
|||
|
||||
// Add textfield to show current directory
|
||||
xpos = HBORDER;
|
||||
ypos += myList->getHeight() + VGAP * 2;
|
||||
lwidth = font.getStringWidth("Path") + LBL_GAP;
|
||||
myDirLabel = new StaticTextWidget(this, font, xpos, ypos+2, lwidth, fontHeight,
|
||||
ypos += myList->getHeight() + VGAP;
|
||||
lwSelect = font.getStringWidth("Path") + LBL_GAP;
|
||||
myDirLabel = new StaticTextWidget(this, font, xpos, ypos+2, lwSelect, fontHeight,
|
||||
"Path", TextAlign::Left);
|
||||
xpos += lwidth;
|
||||
xpos += lwSelect;
|
||||
myDir = new EditTextWidget(this, font, xpos, ypos, _w - xpos - HBORDER, lineHeight, "");
|
||||
myDir->setEditable(false, true);
|
||||
myDir->clearFlags(Widget::FLAG_RETAIN_FOCUS);
|
||||
|
@ -236,9 +306,13 @@ LauncherDialog::LauncherDialog(OSystem& osystem, DialogContainer& parent,
|
|||
myGlobalProps = make_unique<GlobalPropsDialog>(this,
|
||||
myUseMinimalUI ? osystem.frameBuffer().launcherFont() : osystem.frameBuffer().font());
|
||||
|
||||
// since we cannot know how many files there are, use are really high value here
|
||||
myList->progress().setRange(0, 50000, 5);
|
||||
myList->progress().setMessage(" Filtering files" + ELLIPSIS + " ");
|
||||
|
||||
// Do we show only ROMs or all files?
|
||||
bool onlyROMs = instance().settings().getBool("launcherroms");
|
||||
showOnlyROMs(onlyROMs);
|
||||
|
||||
if(myAllFiles)
|
||||
myAllFiles->setState(!onlyROMs);
|
||||
}
|
||||
|
@ -341,7 +415,7 @@ void LauncherDialog::updateUI()
|
|||
|
||||
// Indicate how many files were found
|
||||
ostringstream buf;
|
||||
buf << (myList->getList().size() - 1) << " items found";
|
||||
buf << (myList->getList().size() - 1) << (myShortCount ? " found" : " items found");
|
||||
myRomCount->setLabel(buf.str());
|
||||
|
||||
// Update ROM info UI item
|
||||
|
@ -422,6 +496,7 @@ void LauncherDialog::applyFiltering()
|
|||
{
|
||||
myList->setNameFilter(
|
||||
[&](const FilesystemNode& node) {
|
||||
myList->incProgress();
|
||||
if(!node.isDirectory())
|
||||
{
|
||||
// Do we want to show only ROMs or all files?
|
||||
|
@ -664,6 +739,11 @@ void LauncherDialog::handleCommand(CommandSender* sender, int cmd,
|
|||
reload();
|
||||
break;
|
||||
|
||||
case kSubDirsCmd:
|
||||
myList->setIncludeSubDirs(mySubDirs->getState());
|
||||
reload();
|
||||
break;
|
||||
|
||||
case kLoadROMCmd:
|
||||
case FileListWidget::ItemActivated:
|
||||
saveConfig();
|
||||
|
@ -689,9 +769,15 @@ void LauncherDialog::handleCommand(CommandSender* sender, int cmd,
|
|||
break;
|
||||
|
||||
case EditableWidget::kChangedCmd:
|
||||
{
|
||||
bool subAllowed = myPattern->getText().length() >= MIN_SUBDIRS_CHARS;
|
||||
|
||||
mySubDirs->setEnabled(subAllowed);
|
||||
myList->setIncludeSubDirs(mySubDirs->getState() && subAllowed);
|
||||
applyFiltering(); // pattern matching taken care of directly in this method
|
||||
reload();
|
||||
break;
|
||||
}
|
||||
|
||||
case kQuitCmd:
|
||||
saveConfig();
|
||||
|
|
|
@ -100,6 +100,7 @@ class LauncherDialog : public Dialog
|
|||
static constexpr int MIN_ROMINFO_CHARS = 30;
|
||||
static constexpr int MIN_ROMINFO_ROWS = 7; // full lines
|
||||
static constexpr int MIN_ROMINFO_LINES = 4; // extra lines
|
||||
static constexpr int MIN_SUBDIRS_CHARS = 3; // minimum filter chars for subdirectory search
|
||||
|
||||
void setPosition() override { positionAt(0); }
|
||||
void handleKeyDown(StellaKey key, StellaMod mod, bool repeated) override;
|
||||
|
@ -169,19 +170,22 @@ class LauncherDialog : public Dialog
|
|||
// automatically sized font for ROM info viewer
|
||||
unique_ptr<GUI::Font> myROMInfoFont;
|
||||
|
||||
ButtonWidget* myStartButton{nullptr};
|
||||
ButtonWidget* myPrevDirButton{nullptr};
|
||||
ButtonWidget* myOptionsButton{nullptr};
|
||||
ButtonWidget* myQuitButton{nullptr};
|
||||
CheckboxWidget* myAllFiles{nullptr};
|
||||
EditTextWidget* myPattern{nullptr};
|
||||
CheckboxWidget* mySubDirs{nullptr};
|
||||
StaticTextWidget* myRomCount{nullptr};
|
||||
|
||||
FileListWidget* myList{nullptr};
|
||||
|
||||
StaticTextWidget* myDirLabel{nullptr};
|
||||
EditTextWidget* myDir{nullptr};
|
||||
StaticTextWidget* myRomCount{nullptr};
|
||||
EditTextWidget* myPattern{nullptr};
|
||||
CheckboxWidget* myAllFiles{nullptr};
|
||||
|
||||
RomInfoWidget* myRomInfoWidget{nullptr};
|
||||
ButtonWidget* myStartButton{nullptr};
|
||||
ButtonWidget* myPrevDirButton{nullptr};
|
||||
ButtonWidget* myOptionsButton{nullptr};
|
||||
ButtonWidget* myQuitButton{nullptr};
|
||||
|
||||
RomInfoWidget* myRomInfoWidget{nullptr};
|
||||
std::unordered_map<string,string> myMD5List;
|
||||
|
||||
int mySelectedItem{0};
|
||||
|
@ -189,9 +193,11 @@ class LauncherDialog : public Dialog
|
|||
bool myShowOnlyROMs{false};
|
||||
bool myUseMinimalUI{false};
|
||||
bool myEventHandled{false};
|
||||
bool myShortCount{false};
|
||||
|
||||
enum {
|
||||
kAllfilesCmd = 'lalf', // show all files (or ROMs only)
|
||||
kSubDirsCmd = 'lred',
|
||||
kPrevDirCmd = 'PRVD',
|
||||
kOptionsCmd = 'OPTI',
|
||||
kQuitCmd = 'QUIT'
|
||||
|
|
|
@ -26,8 +26,9 @@
|
|||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
ProgressDialog::ProgressDialog(GuiObject* boss, const GUI::Font& font,
|
||||
const string& message)
|
||||
: Dialog(boss->instance(), boss->parent())
|
||||
const string& message, bool openDialog)
|
||||
: Dialog(boss->instance(), boss->parent()),
|
||||
myFont(font)
|
||||
{
|
||||
const int fontWidth = font.getMaxCharWidth(),
|
||||
fontHeight = font.getFontHeight(),
|
||||
|
@ -52,13 +53,23 @@ ProgressDialog::ProgressDialog(GuiObject* boss, const GUI::Font& font,
|
|||
mySlider->setMinValue(1);
|
||||
mySlider->setMaxValue(100);
|
||||
|
||||
open();
|
||||
if(openDialog)
|
||||
open();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void ProgressDialog::setMessage(const string& message)
|
||||
{
|
||||
const int fontWidth = myFont.getMaxCharWidth(),
|
||||
HBORDER = fontWidth * 1.25;
|
||||
const int lwidth = myFont.getStringWidth(message);
|
||||
|
||||
// Recalculate real dimensions
|
||||
_w = HBORDER * 2 + lwidth;
|
||||
|
||||
myMessage->setWidth(lwidth);
|
||||
myMessage->setLabel(message);
|
||||
mySlider->setWidth(lwidth);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -68,17 +79,25 @@ void ProgressDialog::setRange(int start, int finish, int step)
|
|||
myFinish = finish;
|
||||
myStep = int((step / 100.0) * (myFinish - myStart + 1));
|
||||
|
||||
mySlider->setMinValue(myStart);
|
||||
mySlider->setMinValue(myStart + myStep);
|
||||
mySlider->setMaxValue(myFinish);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void ProgressDialog::resetProgress()
|
||||
{
|
||||
myProgress = myStepProgress = 0;
|
||||
mySlider->setValue(0);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void ProgressDialog::setProgress(int progress)
|
||||
{
|
||||
// Only increase the progress bar if we have arrived at a new step
|
||||
if(progress - mySlider->getValue() > myStep)
|
||||
if(progress - myStepProgress >= myStep)
|
||||
{
|
||||
mySlider->setValue(progress);
|
||||
myStepProgress = progress;
|
||||
mySlider->setValue(progress % (myFinish - myStart + 1));
|
||||
|
||||
// Since this dialog is usually called in a tight loop that doesn't
|
||||
// yield, we need to manually tell the framebuffer that a redraw is
|
||||
|
@ -88,3 +107,9 @@ void ProgressDialog::setProgress(int progress)
|
|||
instance().frameBuffer().update();
|
||||
}
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void ProgressDialog::incProgress()
|
||||
{
|
||||
setProgress(++myProgress);
|
||||
}
|
||||
|
|
|
@ -23,23 +23,29 @@ class StaticTextWidget;
|
|||
class SliderWidget;
|
||||
|
||||
#include "bspf.hxx"
|
||||
#include "Dialog.hxx"
|
||||
|
||||
class ProgressDialog : public Dialog
|
||||
{
|
||||
public:
|
||||
ProgressDialog(GuiObject* boss, const GUI::Font& font,
|
||||
const string& message);
|
||||
const string& message, bool openDialog = true);
|
||||
~ProgressDialog() override = default;
|
||||
|
||||
void setMessage(const string& message);
|
||||
void setRange(int begin, int end, int step);
|
||||
void resetProgress();
|
||||
void setProgress(int progress);
|
||||
void incProgress();
|
||||
|
||||
private:
|
||||
const GUI::Font& myFont;
|
||||
StaticTextWidget* myMessage{nullptr};
|
||||
SliderWidget* mySlider{nullptr};
|
||||
|
||||
int myStart{0}, myFinish{0}, myStep{0};
|
||||
int myProgress{0};
|
||||
int myStepProgress{0};
|
||||
|
||||
private:
|
||||
// Following constructors and assignment operators not supported
|
||||
|
|
|
@ -179,5 +179,5 @@ void ToolTip::show(const string& tip)
|
|||
void ToolTip::render()
|
||||
{
|
||||
if(myTipShown)
|
||||
mySurface->render(), cerr << " render tooltip" << endl;
|
||||
mySurface->render(); // , cerr << " render tooltip" << endl;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue