mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-08-13 21:24:37 +02:00
v108.3
* 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:
@@ -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";
|
||||||
|
@@ -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;
|
||||||
|
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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
|
||||||
|
|
||||||
|
@@ -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));
|
||||||
|
@@ -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
|
||||||
}
|
}
|
||||||
|
@@ -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);
|
||||||
|
@@ -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;
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user