* add support for TableView::onActivate(TableViewCell) to Windows, macOS
** allows the new input/hotkey mapping panels to work on Windows, macOS
* polish BrowserDialog behavior
This commit is contained in:
byuu
2019-08-01 02:40:35 +09:00
parent a7b30b069c
commit bc7456246c
8 changed files with 80 additions and 28 deletions

View File

@@ -31,7 +31,7 @@ using namespace nall;
namespace Emulator { namespace Emulator {
static const string Name = "bsnes"; static const string Name = "bsnes";
static const string Version = "108.2"; static const string Version = "108.3";
static const string Author = "byuu"; static const string Author = "byuu";
static const string License = "GPLv3"; static const string License = "GPLv3";
static const string Website = "https://byuu.org"; static const string Website = "https://byuu.org";

View File

@@ -1,9 +1,4 @@
#if defined(PLATFORM_WINDOWS) || defined(PLATFORM_MACOS)
//TODO: hiro support for TableView::activate(TableViewCell) is required for > 1 binding
enum : uint { BindingLimit = 1 };
#else
enum : uint { BindingLimit = 4 }; enum : uint { BindingLimit = 4 };
#endif
struct InputMapping { struct InputMapping {
auto bind() -> void; auto bind() -> void;

View File

@@ -110,13 +110,15 @@
tableView->doChange(); tableView->doChange();
} }
-(IBAction) activate:(id)sender {
tableView->doActivate({});
}
-(IBAction) doubleAction:(id)sender { -(IBAction) doubleAction:(id)sender {
if([content clickedRow] >= 0) { int row = [content clickedRow];
[self activate:self]; if(row >= 0 && row < tableView->state.items.size()) {
int column = [content clickedColumn];
if(column >= 0 && column < tableView->state.columns.size()) {
auto item = tableView->state.items[row];
auto cell = item->cell(column);
tableView->doActivate(cell);
}
} }
} }
@@ -127,9 +129,14 @@
-(void) keyDown:(NSEvent*)event { -(void) keyDown:(NSEvent*)event {
auto character = [[event characters] characterAtIndex:0]; auto character = [[event characters] characterAtIndex:0];
if(character == NSEnterCharacter || character == NSCarriageReturnCharacter) { if(character == NSEnterCharacter || character == NSCarriageReturnCharacter) {
if([self selectedRow] >= 0) { int row = [self selectedRow];
[[self delegate] activate:self]; if(row >= 0 && row < tableView->state.items.size()) {
return; int column = max(0, [self selectedColumn]); //can be -1?
if(column >= 0 && column < tableView->state.columns.size()) {
auto item = tableView->state.items[row];
auto cell = item->cell(column);
tableView->doActivate(cell);
}
} }
} }

View File

@@ -20,7 +20,6 @@
-(NSString*) tableView:(NSTableView*)table toolTipForCell:(NSCell*)cell rect:(NSRectPointer)rect tableColumn:(NSTableColumn*)tableColumn row:(NSInteger)row mouseLocation:(NSPoint)mouseLocation; -(NSString*) tableView:(NSTableView*)table toolTipForCell:(NSCell*)cell rect:(NSRectPointer)rect tableColumn:(NSTableColumn*)tableColumn row:(NSInteger)row mouseLocation:(NSPoint)mouseLocation;
-(void) tableView:(NSTableView*)tableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn*)tableColumn row:(NSInteger)row; -(void) tableView:(NSTableView*)tableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn*)tableColumn row:(NSInteger)row;
-(void) tableViewSelectionDidChange:(NSNotification*)notification; -(void) tableViewSelectionDidChange:(NSNotification*)notification;
-(IBAction) activate:(id)sender;
-(IBAction) doubleAction:(id)sender; -(IBAction) doubleAction:(id)sender;
@end @end

View File

@@ -8,6 +8,8 @@ struct BrowserDialogWindow {
auto activate() -> void; auto activate() -> void;
auto change() -> void; auto change() -> void;
auto context() -> void; auto context() -> void;
auto isObject(const string& name) -> bool;
auto isFile(const string& name) -> bool;
auto isFolder(const string& name) -> bool; auto isFolder(const string& name) -> bool;
auto isMatch(const string& name) -> bool; auto isMatch(const string& name) -> bool;
auto run() -> BrowserDialog::Response; auto run() -> BrowserDialog::Response;
@@ -43,7 +45,7 @@ private:
vector<vector<string>> filters; vector<vector<string>> filters;
}; };
//accept button clicked, or enter pressed on file name line edit //accept button clicked, or enter pressed inside file name field
//also called by table view activate after special case handling //also called by table view activate after special case handling
auto BrowserDialogWindow::accept() -> void { auto BrowserDialogWindow::accept() -> void {
auto batched = view.batched(); auto batched = view.batched();
@@ -51,7 +53,7 @@ auto BrowserDialogWindow::accept() -> void {
if(state.action == "openFile" && batched.size() == 1) { if(state.action == "openFile" && batched.size() == 1) {
string name = batched[0].text(); string name = batched[0].text();
if(isFolder(name)) return setPath({state.path, name}); if(isFolder(name)) return setPath({state.path, name});
response.selected.append(string{state.path, name}); response.selected.append({state.path, name});
} }
if(state.action == "openFiles" && batched) { if(state.action == "openFiles" && batched) {
@@ -63,20 +65,20 @@ auto BrowserDialogWindow::accept() -> void {
response.selected.reset(); response.selected.reset();
return; return;
} }
response.selected.append(string{state.path, name}); response.selected.append({state.path, name});
} }
} }
if(state.action == "openFolder" && batched.size() == 1) { if(state.action == "openFolder" && batched.size() == 1) {
string name = batched[0].text(); string name = batched[0].text();
if(!isMatch(name)) return setPath({state.path, name}); if(!isMatch(name)) return setPath({state.path, name});
response.selected.append(string{state.path, name, "/"}); response.selected.append({state.path, name, "/"});
} }
if(state.action == "openObject" && batched.size() == 1) { if(state.action == "openObject" && batched.size() == 1) {
string name = batched[0].text(); string name = batched[0].text();
if(!isMatch(name) && isFolder(name)) return setPath({state.path, name}); if(!isMatch(name) && isFolder(name)) return setPath({state.path, name});
response.selected.append(string{state.path, name, isFolder(name) ? "/" : ""}); response.selected.append({state.path, name, isFolder(name) ? "/" : ""});
} }
if(state.action == "saveFile") { if(state.action == "saveFile") {
@@ -85,7 +87,7 @@ auto BrowserDialogWindow::accept() -> void {
if(file::exists({state.path, name})) { if(file::exists({state.path, name})) {
if(MessageDialog("File already exists. Overwrite it?").question() != "Yes") return; if(MessageDialog("File already exists. Overwrite it?").question() != "Yes") return;
} }
response.selected.append(string{state.path, name}); response.selected.append({state.path, name});
} }
if(state.action == "selectFolder") { if(state.action == "selectFolder") {
@@ -93,7 +95,7 @@ auto BrowserDialogWindow::accept() -> void {
response.selected.append(state.path); response.selected.append(state.path);
} else if(batched.size() == 1) { } else if(batched.size() == 1) {
string name = batched[0].text(); string name = batched[0].text();
if(isFolder(name)) response.selected.append(string{state.path, name, "/"}); if(isFolder(name)) response.selected.append({state.path, name, "/"});
} }
} }
@@ -137,9 +139,13 @@ auto BrowserDialogWindow::change() -> void {
acceptButton.setEnabled(batched.size() == 1); acceptButton.setEnabled(batched.size() == 1);
} }
if(state.action == "saveFile") { if(state.action == "saveFile") {
string result;
if(batched.size() == 1) { if(batched.size() == 1) {
string name = batched[0].text(); string name = batched[0].text();
if(!isFolder(name)) fileName.setText(name).doChange(); if(!isFolder(name)) result = name;
}
if(result != fileName.text()) {
fileName.setText(result).doChange();
} }
} }
if(state.action == "selectFolder") { if(state.action == "selectFolder") {
@@ -165,6 +171,14 @@ auto BrowserDialogWindow::context() -> void {
contextMenu.setVisible(); contextMenu.setVisible();
} }
auto BrowserDialogWindow::isObject(const string& name) -> bool {
return inode::exists({state.path, name});
}
auto BrowserDialogWindow::isFile(const string& name) -> bool {
return file::exists({state.path, name});
}
auto BrowserDialogWindow::isFolder(const string& name) -> bool { auto BrowserDialogWindow::isFolder(const string& name) -> bool {
return directory::exists({state.path, name}); return directory::exists({state.path, name});
} }
@@ -217,8 +231,21 @@ auto BrowserDialogWindow::run() -> BrowserDialog::Response {
iconSearch.scale(16_sx, 16_sy); iconSearch.scale(16_sx, 16_sy);
searchButton.setIcon(iconSearch).setBordered(false).onActivate([&] { setPath(state.path, fileName.text()); }); searchButton.setIcon(iconSearch).setBordered(false).onActivate([&] { setPath(state.path, fileName.text()); });
fileName.onActivate([&] { fileName.onActivate([&] {
string name = fileName.text();
if((state.action == "openFile" || state.action == "openFiles") && isFile(name)) {
response.selected.append({state.path, name});
return (void)window.setModal(false);
}
if((state.action == "openFolder" || state.action == "selectFolder") && isFolder(name)) {
response.selected.append({state.path, name});
return (void)window.setModal(false);
}
if(state.action == "openObject" && isObject(name)) {
response.selected.append({state.path, name});
return (void)window.setModal(false);
}
if(state.action == "saveFile") return accept(); if(state.action == "saveFile") return accept();
setPath(state.path, fileName.text()); setPath(state.path, name);
}).onChange([&] { }).onChange([&] {
auto name = fileName.text(); auto name = fileName.text();
if(state.action == "saveFile") acceptButton.setEnabled(name && !isFolder(name)); if(state.action == "saveFile") acceptButton.setEnabled(name && !isFolder(name));

View File

@@ -447,18 +447,26 @@ static auto CALLBACK Shared_windowProc(WindowProc windowProc, HWND hwnd, UINT ms
#if defined(Hiro_TableView) #if defined(Hiro_TableView)
case AppMessage::TableView_doPaint: { case AppMessage::TableView_doPaint: {
if(auto tableView = (mTableView*)lparam) { if(auto tableView = (mTableView*)lparam) {
if(auto self = tableView->self()) InvalidateRect(self->hwnd, nullptr, true); if(auto self = tableView->self()) {
InvalidateRect(self->hwnd, nullptr, true);
}
} }
break; break;
} }
case AppMessage::TableView_onActivate: { case AppMessage::TableView_onActivate: {
if(auto tableView = (mTableView*)lparam) tableView->doActivate({}); if(auto tableView = (mTableView*)lparam) {
if(auto self = tableView->self()) {
tableView->doActivate(self->activateCell);
}
}
break; break;
} }
case AppMessage::TableView_onChange: { case AppMessage::TableView_onChange: {
if(auto tableView = (mTableView*)lparam) tableView->doChange(); if(auto tableView = (mTableView*)lparam) {
tableView->doChange();
}
} }
#endif #endif
} }

View File

@@ -105,6 +105,21 @@ auto pTableView::onActivate(LPARAM lparam) -> void {
auto nmlistview = (LPNMLISTVIEW)lparam; auto nmlistview = (LPNMLISTVIEW)lparam;
if(ListView_GetSelectedCount(hwnd) == 0) return; if(ListView_GetSelectedCount(hwnd) == 0) return;
if(!locked()) { if(!locked()) {
activateCell = TableViewCell();
LVHITTESTINFO hitTest{};
GetCursorPos(&hitTest.pt);
ScreenToClient(nmlistview->hdr.hwndFrom, &hitTest.pt);
ListView_SubItemHitTest(nmlistview->hdr.hwndFrom, &hitTest);
if(hitTest.flags & LVHT_ONITEM) {
int row = hitTest.iItem;
if(row >= 0 && row < state().items.size()) {
int column = hitTest.iSubItem;
if(column >= 0 && column < state().columns.size()) {
auto item = state().items[row];
activateCell = item->cell(column);
}
}
}
//LVN_ITEMACTIVATE is not re-entrant until DispatchMessage() completes //LVN_ITEMACTIVATE is not re-entrant until DispatchMessage() completes
//thus, we don't call self().doActivate() here //thus, we don't call self().doActivate() here
PostMessageOnce(_parentHandle(), AppMessage::TableView_onActivate, 0, (LPARAM)&reference); PostMessageOnce(_parentHandle(), AppMessage::TableView_onActivate, 0, (LPARAM)&reference);

View File

@@ -35,6 +35,7 @@ struct pTableView : pWidget {
auto _setIcons() -> void; auto _setIcons() -> void;
auto _width(unsigned column) -> unsigned; auto _width(unsigned column) -> unsigned;
TableViewCell activateCell;
HIMAGELIST imageList = 0; HIMAGELIST imageList = 0;
vector<image> icons; vector<image> icons;
}; };