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:
Tim Allen
2015-05-30 21:39:09 +10:00
parent 7bf4cff946
commit 314aee8c5c
40 changed files with 500 additions and 545 deletions

View File

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

View File

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

View File

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

View File

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