mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-09-03 14:52:35 +02:00
Update to v094r23 release.
byuu says: The library window is gone, and replaced with hiro::BrowserWindow::openFolder(). This gives navigation capabilities to game loading, and it also completes our slotted cart selection code. As an added bonus, it's less code this way, too. I also set the window size to consistent sizes between all emulated systems, so that switching between SFC and GB don't cause the window size to keep changing, and so that the scaling size is consistent (eg at normal scale, GB @ 3x is closer to SNES @ 2x.) This means black borders in GB/GBA mode, but it doesn't look that bad, and it's not like many people ever use these modes anyway. Finally, added the placeholder tabs for video, audio and timing. I don't intend to add the timing calculator code to v095 (it might be better as a separate tool), but I'll add the ability to set video/audio rates, at least. Glitch 1: despite selecting the first item in the BrowserDialog list, if you press enter when the window appears, it doesn't activate the item until you press an arrow key first. Glitch 2: in Game Boy mode, if you set the 4x window size, it's not honoring the full requested height because the viewport is smaller than the window. 8+ years of trying to get GTK+ and Qt to simply set the god damned window size I ask for, and I still can't get them to do it reliably. Remaining issues: - finish configuration panels (video, audio, timing) - fix ruby driver compilation on Windows - add DIP switch selection window (NSS) [I may end up punting this one to v096]
This commit is contained in:
@@ -6,6 +6,7 @@ struct BrowserDialogWindow {
|
||||
auto activate() -> void;
|
||||
auto change() -> void;
|
||||
auto isFolder(const string& name) -> bool;
|
||||
auto isMatch(const string& name) -> bool;
|
||||
auto run() -> lstring;
|
||||
auto setPath(string path) -> void;
|
||||
|
||||
@@ -25,6 +26,7 @@ private:
|
||||
Button cancelButton{&controlLayout, Size{80, 0}, 8};
|
||||
|
||||
BrowserDialog::State& state;
|
||||
vector<lstring> filters;
|
||||
};
|
||||
|
||||
//accept button clicked, or enter pressed on file name line edit
|
||||
@@ -45,12 +47,18 @@ auto BrowserDialogWindow::accept() -> void {
|
||||
}
|
||||
}
|
||||
|
||||
if(state.action == "openFolder" && selectedItems) {
|
||||
string name = selectedItems.first()->text(0);
|
||||
if(!isMatch(name)) return setPath({state.path, name});
|
||||
state.response.append(string{state.path, name, "/"});
|
||||
}
|
||||
|
||||
if(state.action == "saveFile") {
|
||||
string name = fileName.text();
|
||||
if(!name && selectedItems) name = selectedItems.first()->text(0);
|
||||
if(!name || isFolder(name)) return;
|
||||
if(file::exists({state.path, name})) {
|
||||
if(MessageDialog("File already exists; overwrite it?").question() != 0) return;
|
||||
if(MessageDialog("File already exists; overwrite it?").question() != "Yes") return;
|
||||
}
|
||||
state.response.append(string{state.path, name});
|
||||
}
|
||||
@@ -66,15 +74,18 @@ auto BrowserDialogWindow::accept() -> void {
|
||||
//list view item double-clicked, or enter pressed on selected list view item
|
||||
auto BrowserDialogWindow::activate() -> void {
|
||||
auto selectedItem = view.selected();
|
||||
|
||||
if(state.action == "saveFile" && selectedItem) {
|
||||
string name = selectedItem->text(0);
|
||||
if(isFolder(name)) return setPath({state.path, name});
|
||||
fileName.setText(isFolder(name) ? "" : name);
|
||||
fileName.setText(name);
|
||||
}
|
||||
|
||||
if(state.action == "selectFolder" && selectedItem) {
|
||||
string name = selectedItem->text(0);
|
||||
if(isFolder(name)) return setPath({state.path, name});
|
||||
}
|
||||
|
||||
accept();
|
||||
}
|
||||
|
||||
@@ -93,34 +104,41 @@ auto BrowserDialogWindow::isFolder(const string& name) -> bool {
|
||||
return directory::exists({state.path, name});
|
||||
}
|
||||
|
||||
auto BrowserDialogWindow::isMatch(const string& name) -> bool {
|
||||
if(auto selectedItem = filterList.selected()) {
|
||||
for(auto& filter : filters[selectedItem->offset()]) {
|
||||
if(name.match(filter)) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
auto BrowserDialogWindow::run() -> lstring {
|
||||
state.response.reset();
|
||||
|
||||
layout.setMargin(8);
|
||||
pathName.onActivate([&] { setPath(pathName.text()); });
|
||||
pathHome.onActivate([&] { setPath(userpath()); });
|
||||
pathHome.setBordered(false).setIcon(Icon::Go::Home);
|
||||
pathRefresh.onActivate([&] { setPath(state.path); });
|
||||
pathRefresh.setBordered(false).setIcon(Icon::Action::Refresh);
|
||||
pathUp.onActivate([&] { setPath(state.path.dirname()); });
|
||||
pathUp.setBordered(false).setIcon(Icon::Go::Up);
|
||||
view.onActivate([&] { activate(); });
|
||||
view.onChange([&] { change(); });
|
||||
view.setMultiSelect(state.action == "openFiles");
|
||||
filterList.onChange([&] { setPath(state.path); });
|
||||
pathHome.setBordered(false).setIcon(Icon::Go::Home).onActivate([&] { setPath(userpath()); });
|
||||
pathRefresh.setBordered(false).setIcon(Icon::Action::Refresh).onActivate([&] { setPath(state.path); });
|
||||
pathUp.setBordered(false).setIcon(Icon::Go::Up).onActivate([&] { setPath(state.path.dirname()); });
|
||||
view.setMultiSelect(state.action == "openFiles").onActivate([&] { activate(); }).onChange([&] { change(); });
|
||||
filterList.setVisible(state.action != "selectFolder").onChange([&] { setPath(state.path); });
|
||||
for(auto& filter : state.filters) {
|
||||
auto part = filter.split<1>("|");
|
||||
filterList.append(ComboButtonItem().setText(part.first()));
|
||||
}
|
||||
filterList.setVisible(state.action != "selectFolder");
|
||||
fileName.onActivate([&] { accept(); });
|
||||
fileName.setVisible(state.action == "saveFile");
|
||||
fileName.setVisible(state.action == "saveFile").onActivate([&] { accept(); });
|
||||
acceptButton.onActivate([&] { accept(); });
|
||||
if(state.action == "openFile" || state.action == "openFiles") acceptButton.setText("Open");
|
||||
if(state.action == "openFile" || state.action == "openFiles" || state.action == "openFolder") acceptButton.setText("Open");
|
||||
if(state.action == "saveFile") acceptButton.setText("Save");
|
||||
if(state.action == "selectFolder") acceptButton.setText("Select");
|
||||
cancelButton.onActivate([&] { window.setModal(false); });
|
||||
cancelButton.setText("Cancel");
|
||||
cancelButton.setText("Cancel").onActivate([&] { window.setModal(false); });
|
||||
|
||||
if(!state.filters) state.filters.append("All|*");
|
||||
for(auto& filter : state.filters) {
|
||||
auto part = filter.split<1>("|");
|
||||
filters.append(part.last().split(":"));
|
||||
}
|
||||
|
||||
setPath(state.path);
|
||||
|
||||
@@ -146,30 +164,30 @@ auto BrowserDialogWindow::setPath(string path) -> void {
|
||||
view.append(ListViewColumn().setWidth(~0));
|
||||
view.append(ListViewColumn().setWidth( 0).setForegroundColor({192, 128, 128}));
|
||||
|
||||
for(auto& folder : directory::folders(path)) {
|
||||
auto contents = directory::contents(path);
|
||||
bool folderMode = state.action == "openFolder";
|
||||
|
||||
for(auto content : contents) {
|
||||
if(!content.endsWith("/")) continue;
|
||||
if(folderMode && isMatch(content.rtrim("/"))) continue;
|
||||
|
||||
ListViewItem item{&view};
|
||||
item.setIcon(0, Icon::Emblem::Folder);
|
||||
item.setText(0, folder.rtrim("/"));
|
||||
item.setText(1, octal<3>(storage::mode({path, folder}) & 0777));
|
||||
item.setText(0, content.rtrim("/"));
|
||||
item.setText(1, octal<3>(storage::mode({path, content}) & 0777));
|
||||
}
|
||||
|
||||
if(state.action != "selectFolder") { //don't show files during folder selection
|
||||
string filter = "*";
|
||||
if(auto selected = filterList.selected()) {
|
||||
auto part = state.filters[selected->offset()].split<1>("|");
|
||||
filter = part.last();
|
||||
}
|
||||
for(auto content : contents) {
|
||||
if(content.endsWith("/") && !folderMode) continue;
|
||||
if(!isMatch(content.rtrim("/"))) continue;
|
||||
|
||||
for(auto& file : directory::files(path)) {
|
||||
if(!file.match(filter)) continue;
|
||||
ListViewItem item{&view};
|
||||
item.setIcon(0, Icon::Emblem::File);
|
||||
item.setText(0, file);
|
||||
item.setText(1, octal<3>(storage::mode({path, file}) & 0777));
|
||||
}
|
||||
ListViewItem item{&view};
|
||||
item.setIcon(0, folderMode ? Icon::Action::Open : Icon::Emblem::File);
|
||||
item.setText(0, content.rtrim("/"));
|
||||
item.setText(1, octal<3>(storage::mode({path, content}) & 0777));
|
||||
}
|
||||
|
||||
if(view.items()) view.setSelected({0});
|
||||
if(view.items()) view.item(0)->setSelected();
|
||||
Application::processEvents();
|
||||
view.resizeColumns().setFocused().doChange();
|
||||
}
|
||||
@@ -191,7 +209,14 @@ auto BrowserDialog::openFiles() -> lstring {
|
||||
if(!state.title) state.title = "Open Files";
|
||||
if(auto result = _run()) return result;
|
||||
return {};
|
||||
};
|
||||
}
|
||||
|
||||
auto BrowserDialog::openFolder() -> string {
|
||||
state.action = "openFolder";
|
||||
if(!state.title) state.title = "Open Folder";
|
||||
if(auto result = _run()) return result.first();
|
||||
return {};
|
||||
}
|
||||
|
||||
auto BrowserDialog::saveFile() -> string {
|
||||
state.action = "saveFile";
|
||||
@@ -212,7 +237,7 @@ auto BrowserDialog::setFilters(const lstring& filters) -> type& {
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto BrowserDialog::setParent(const shared_pointer<mWindow>& parent) -> type& {
|
||||
auto BrowserDialog::setParent(const sWindow& parent) -> type& {
|
||||
state.parent = parent;
|
||||
return *this;
|
||||
}
|
||||
|
@@ -6,26 +6,27 @@ struct BrowserDialog {
|
||||
using type = BrowserDialog;
|
||||
|
||||
BrowserDialog();
|
||||
auto openFile() -> nall::string; //one existing file
|
||||
auto openFiles() -> nall::lstring; //any existing files or folders
|
||||
auto saveFile() -> nall::string; //one file
|
||||
auto selectFolder() -> nall::string; //one existing folder
|
||||
auto setFilters(const nall::lstring& filters = {"All|*"}) -> type&;
|
||||
auto setParent(const nall::shared_pointer<mWindow>& parent) -> type&;
|
||||
auto setPath(const nall::string& path = "") -> type&;
|
||||
auto setTitle(const nall::string& title = "") -> type&;
|
||||
auto openFile() -> string; //one existing file
|
||||
auto openFiles() -> lstring; //any existing files or folders
|
||||
auto openFolder() -> string; //one existing folder
|
||||
auto saveFile() -> string; //one file
|
||||
auto selectFolder() -> string; //one existing folder
|
||||
auto setFilters(const lstring& filters = {}) -> type&;
|
||||
auto setParent(const sWindow& parent) -> type&;
|
||||
auto setPath(const string& path = "") -> type&;
|
||||
auto setTitle(const string& title = "") -> type&;
|
||||
|
||||
private:
|
||||
struct State {
|
||||
nall::string action;
|
||||
nall::lstring filters = {"*"};
|
||||
nall::shared_pointer<mWindow> parent;
|
||||
nall::string path;
|
||||
nall::lstring response;
|
||||
nall::string title;
|
||||
string action;
|
||||
lstring filters = {"*"};
|
||||
sWindow parent;
|
||||
string path;
|
||||
lstring response;
|
||||
string title;
|
||||
} state;
|
||||
|
||||
auto _run() -> nall::lstring;
|
||||
auto _run() -> lstring;
|
||||
|
||||
friend class BrowserDialogWindow;
|
||||
};
|
||||
|
@@ -4,19 +4,19 @@ MessageDialog::MessageDialog(const string& text) {
|
||||
state.text = text;
|
||||
}
|
||||
|
||||
auto MessageDialog::error(const lstring& buttons) -> signed {
|
||||
auto MessageDialog::error(const lstring& buttons) -> string {
|
||||
state.buttons = buttons;
|
||||
state.icon = Icon::Prompt::Error;
|
||||
return _run();
|
||||
}
|
||||
|
||||
auto MessageDialog::information(const lstring& buttons) -> signed {
|
||||
auto MessageDialog::information(const lstring& buttons) -> string {
|
||||
state.buttons = buttons;
|
||||
state.icon = Icon::Prompt::Information;
|
||||
return _run();
|
||||
}
|
||||
|
||||
auto MessageDialog::question(const lstring& buttons) -> signed {
|
||||
auto MessageDialog::question(const lstring& buttons) -> string {
|
||||
state.buttons = buttons;
|
||||
state.icon = Icon::Prompt::Question;
|
||||
return _run();
|
||||
@@ -37,13 +37,13 @@ auto MessageDialog::setTitle(const string& title) -> type& {
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto MessageDialog::warning(const lstring& buttons) -> signed {
|
||||
auto MessageDialog::warning(const lstring& buttons) -> string {
|
||||
state.buttons = buttons;
|
||||
state.icon = Icon::Prompt::Warning;
|
||||
return _run();
|
||||
}
|
||||
|
||||
auto MessageDialog::_run() -> signed {
|
||||
auto MessageDialog::_run() -> string {
|
||||
Window window;
|
||||
VerticalLayout layout{&window};
|
||||
HorizontalLayout messageLayout{&layout, Size{~0, 0}, 8};
|
||||
@@ -57,7 +57,7 @@ auto MessageDialog::_run() -> signed {
|
||||
messageText.setText(state.text);
|
||||
for(auto n : range(state.buttons)) {
|
||||
Button button{&controlLayout, Size{80, 0}, 8};
|
||||
button.onActivate([&, n] { state.response = n; window.setModal(false); });
|
||||
button.onActivate([&, n] { state.response = state.buttons[n]; window.setModal(false); });
|
||||
button.setText(state.buttons[n]);
|
||||
button.setFocused(); //the last button will have effective focus
|
||||
}
|
||||
@@ -66,7 +66,7 @@ auto MessageDialog::_run() -> signed {
|
||||
signed widthButtons = 8 + state.buttons.size() * 88;
|
||||
signed width = max(320, widthMessage, widthButtons);
|
||||
|
||||
window.onClose([&] { state.response = -1; window.setModal(false); });
|
||||
window.onClose([&] { window.setModal(false); });
|
||||
window.setTitle(state.title);
|
||||
window.setResizable(false);
|
||||
window.setSize({width, layout.minimumSize().height()});
|
||||
|
@@ -3,26 +3,26 @@
|
||||
struct MessageDialog {
|
||||
using type = MessageDialog;
|
||||
|
||||
MessageDialog(const nall::string& text = "");
|
||||
auto error(const nall::lstring& buttons = {"Ok"}) -> signed;
|
||||
auto information(const nall::lstring& buttons = {"Ok"}) -> signed;
|
||||
auto question(const nall::lstring& buttons = {"Yes", "No"}) -> signed;
|
||||
auto setParent(nall::shared_pointer<mWindow> parent = {}) -> type&;
|
||||
auto setText(const nall::string& text = "") -> type&;
|
||||
auto setTitle(const nall::string& title = "") -> type&;
|
||||
auto warning(const nall::lstring& buttons = {"Ok"}) -> signed;
|
||||
MessageDialog(const string& text = "");
|
||||
auto error(const lstring& buttons = {"Ok"}) -> string;
|
||||
auto information(const lstring& buttons = {"Ok"}) -> string;
|
||||
auto question(const lstring& buttons = {"Yes", "No"}) -> string;
|
||||
auto setParent(sWindow parent = {}) -> type&;
|
||||
auto setText(const string& text = "") -> type&;
|
||||
auto setTitle(const string& title = "") -> type&;
|
||||
auto warning(const lstring& buttons = {"Ok"}) -> string;
|
||||
|
||||
private:
|
||||
struct State {
|
||||
nall::lstring buttons;
|
||||
nall::vector<uint8_t> icon;
|
||||
nall::shared_pointer<mWindow> parent;
|
||||
signed response = -1;
|
||||
nall::string text;
|
||||
nall::string title;
|
||||
lstring buttons;
|
||||
vector<uint8_t> icon;
|
||||
sWindow parent;
|
||||
string response;
|
||||
string text;
|
||||
string title;
|
||||
} state;
|
||||
|
||||
auto _run() -> signed;
|
||||
auto _run() -> string;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
Reference in New Issue
Block a user