Update to v093r04 release.

byuu says:

This version replaces the old folder-browser with a proper game library.
This commit is contained in:
Tim Allen
2013-11-28 21:32:53 +11:00
parent 68eaf53691
commit b4f18c3b47
42 changed files with 419 additions and 378 deletions

View File

@@ -3,7 +3,7 @@
namespace Emulator { namespace Emulator {
static const char Name[] = "higan"; static const char Name[] = "higan";
static const char Version[] = "093.03"; static const char Version[] = "093.04";
static const char Author[] = "byuu"; static const char Author[] = "byuu";
static const char License[] = "GPLv3"; static const char License[] = "GPLv3";
static const char Website[] = "http://byuu.org/"; static const char Website[] = "http://byuu.org/";

View File

@@ -15,7 +15,6 @@ using namespace nall;
namespace phoenix { namespace phoenix {
#include "state.hpp" #include "state.hpp"
#include "utility.cpp"
#include "layout/fixed-layout.cpp" #include "layout/fixed-layout.cpp"
#include "layout/horizontal-layout.cpp" #include "layout/horizontal-layout.cpp"
#include "layout/vertical-layout.cpp" #include "layout/vertical-layout.cpp"

View File

@@ -243,7 +243,6 @@ struct pWidget : public pSizable {
GtkWidget* gtkParent = nullptr; GtkWidget* gtkParent = nullptr;
virtual GtkWidget* container(Widget& widget); virtual GtkWidget* container(Widget& widget);
virtual Position containerOffset();
virtual bool focused(); virtual bool focused();
virtual Size minimumSize(); virtual Size minimumSize();
virtual void setEnabled(bool enabled); virtual void setEnabled(bool enabled);
@@ -545,7 +544,6 @@ struct pTabFrame : public pWidget {
void append(string text, const image& image); void append(string text, const image& image);
GtkWidget* container(Widget& widget); GtkWidget* container(Widget& widget);
Position containerOffset();
Position displacement(); Position displacement();
void remove(unsigned selection); void remove(unsigned selection);
void setEnabled(bool enabled); void setEnabled(bool enabled);

View File

@@ -73,10 +73,6 @@ static Widget* GetParentWidget(Sizable* sizable) {
return nullptr; return nullptr;
} }
static bool HasParentWidget(Sizable* sizable) {
return GetParentWidget(sizable) != nullptr;
}
static Keyboard::Keycode Keysym(unsigned keysym) { static Keyboard::Keycode Keysym(unsigned keysym) {
switch(keysym) { switch(keysym) {
case GDK_Escape: return Keyboard::Keycode::Escape; case GDK_Escape: return Keyboard::Keycode::Escape;

View File

@@ -38,10 +38,6 @@ GtkWidget* pTabFrame::container(Widget& widget) {
return nullptr; return nullptr;
} }
Position pTabFrame::containerOffset() {
return {widget.state.geometry.x + 3, widget.state.geometry.y + 28};
}
Position pTabFrame::displacement() { Position pTabFrame::displacement() {
return {6, 31}; return {6, 31};
} }
@@ -63,8 +59,7 @@ void pTabFrame::setGeometry(Geometry geometry) {
geometry.x += 1, geometry.width -= 5; geometry.x += 1, geometry.width -= 5;
geometry.y += 26, geometry.height -= 31; geometry.y += 26, geometry.height -= 31;
for(auto& layout : tabFrame.state.layout) { for(auto& layout : tabFrame.state.layout) {
if(layout == nullptr) continue; if(layout) layout->setGeometry(geometry);
layout->setGeometry(geometry);
} }
synchronizeLayout(); synchronizeLayout();
} }

View File

@@ -4,10 +4,6 @@ GtkWidget* pWidget::container(Widget& widget) {
return nullptr; return nullptr;
} }
Position pWidget::containerOffset() {
return {0, 0};
}
bool pWidget::focused() { bool pWidget::focused() {
return GTK_WIDGET_HAS_FOCUS(gtkWidget); return GTK_WIDGET_HAS_FOCUS(gtkWidget);
} }

View File

@@ -153,7 +153,7 @@ void pWindow::append(Widget& widget) {
widget.setFont(window.state.widgetFont); widget.setFont(window.state.widgetFont);
} }
if(HasParentWidget(&widget)) { if(GetParentWidget(&widget)) {
widget.p.gtkParent = GetParentWidget(&widget)->p.container(widget); widget.p.gtkParent = GetParentWidget(&widget)->p.container(widget);
} else { } else {
widget.p.gtkParent = formContainer; widget.p.gtkParent = formContainer;

View File

@@ -1,7 +1,7 @@
/**************************************************************************** /****************************************************************************
** Meta object code from reading C++ file 'platform.moc.hpp' ** Meta object code from reading C++ file 'platform.moc.hpp'
** **
** Created: Fri Nov 22 09:50:07 2013 ** Created: Sun Nov 24 07:06:37 2013
** by: The Qt Meta Object Compiler version 63 (Qt 4.8.2) ** by: The Qt Meta Object Compiler version 63 (Qt 4.8.2)
** **
** WARNING! All changes made in this file will be lost! ** WARNING! All changes made in this file will be lost!

View File

@@ -127,8 +127,6 @@ public:
QStatusBar* qtStatus; QStatusBar* qtStatus;
QWidget* qtContainer; QWidget* qtContainer;
static Window& none();
void append(Layout& layout); void append(Layout& layout);
void append(Menu& menu); void append(Menu& menu);
void append(Widget& widget); void append(Widget& widget);
@@ -255,6 +253,8 @@ public slots:
struct pSizable : public pObject { struct pSizable : public pObject {
Sizable& sizable; Sizable& sizable;
virtual Position displacement() { return {0, 0}; }
pSizable(Sizable& sizable) : pObject(sizable), sizable(sizable) {} pSizable(Sizable& sizable) : pObject(sizable), sizable(sizable) {}
void constructor() {} void constructor() {}
@@ -274,6 +274,7 @@ struct pWidget : public pSizable {
Widget& widget; Widget& widget;
QWidget* qtWidget; QWidget* qtWidget;
virtual QWidget* container(Widget& widget);
bool focused(); bool focused();
virtual Size minimumSize(); virtual Size minimumSize();
virtual void setEnabled(bool enabled); virtual void setEnabled(bool enabled);
@@ -664,6 +665,8 @@ public:
QTabWidget* qtTabFrame; QTabWidget* qtTabFrame;
void append(string text, const image& image); void append(string text, const image& image);
QWidget* container(Widget& widget);
Position displacement();
void remove(unsigned selection); void remove(unsigned selection);
void setEnabled(bool enabled); void setEnabled(bool enabled);
void setGeometry(Geometry geometry); void setGeometry(Geometry geometry);

View File

@@ -23,6 +23,33 @@ static lstring DropPaths(QDropEvent* event) {
return paths; return paths;
} }
static Position GetDisplacement(Sizable* sizable) {
Position position;
while(sizable->state.parent) {
Position displacement = sizable->state.parent->p.displacement();
position.x += displacement.x;
position.y += displacement.y;
sizable = sizable->state.parent;
}
return position;
}
static Layout* GetParentWidgetLayout(Sizable* sizable) {
while(sizable) {
if(sizable->state.parent && dynamic_cast<TabFrame*>(sizable->state.parent)) return (Layout*)sizable;
sizable = sizable->state.parent;
}
return nullptr;
}
static Widget* GetParentWidget(Sizable* sizable) {
while(sizable) {
if(sizable->state.parent && dynamic_cast<TabFrame*>(sizable->state.parent)) return (Widget*)sizable->state.parent;
sizable = sizable->state.parent;
}
return nullptr;
}
static Keyboard::Keycode Keysym(int keysym) { static Keyboard::Keycode Keysym(int keysym) {
switch(keysym) { switch(keysym) {
case XK_Escape: return Keyboard::Keycode::Escape; case XK_Escape: return Keyboard::Keycode::Escape;

View File

@@ -10,7 +10,7 @@ void pFrame::setGeometry(Geometry geometry) {
if(frame.state.layout == nullptr) return; if(frame.state.layout == nullptr) return;
Size size = pFont::size(widget.state.font, frame.state.text); Size size = pFont::size(widget.state.font, frame.state.text);
if(frame.state.text.empty()) size.height = 8; if(frame.state.text.empty()) size.height = 8;
geometry.x += 1, geometry.width -= 3; geometry.x += 1, geometry.width -= 2;
geometry.y += size.height, geometry.height -= size.height + 1; geometry.y += size.height, geometry.height -= size.height + 1;
frame.state.layout->setGeometry(geometry); frame.state.layout->setGeometry(geometry);
} }
@@ -26,6 +26,14 @@ void pFrame::setVisible(bool visible) {
void pFrame::constructor() { void pFrame::constructor() {
qtWidget = qtFrame = new QGroupBox; qtWidget = qtFrame = new QGroupBox;
if(QApplication::style()->objectName() == "gtk+") {
//QGtkStyle (gtk+) theme disrespects font weight and omits the border, even if native GTK+ theme does not
//bold Label controls already exist; so this style sheet forces QGtkStyle to look like a Frame instead
qtFrame->setStyleSheet(
"QGroupBox { border: 1px solid #aaa; border-radius: 5px; margin-top: 0.5em; }\n"
"QGroupBox::title { left: 5px; subcontrol-origin: margin; }\n"
);
}
pWidget::synchronizeState(); pWidget::synchronizeState();
setText(frame.state.text); setText(frame.state.text);

View File

@@ -2,11 +2,24 @@ namespace phoenix {
void pTabFrame::append(string text, const image& image) { void pTabFrame::append(string text, const image& image) {
unsigned selection = tabFrame.state.text.size() - 1; unsigned selection = tabFrame.state.text.size() - 1;
QWidget* widget = new QWidget; //addTab() requires a child widget, so give it an empty one qtTabFrame->addTab(new QWidget, QString::fromUtf8(text));
qtTabFrame->addTab(widget, QString::fromUtf8(text));
if(!image.empty()) setImage(selection, image); if(!image.empty()) setImage(selection, image);
} }
QWidget* pTabFrame::container(Widget& widget) {
Layout* widgetLayout = GetParentWidgetLayout(&widget);
unsigned selection = 0;
for(auto& layout : tabFrame.state.layout) {
if(layout == widgetLayout) return qtTabFrame->widget(selection);
selection++;
}
return nullptr;
}
Position pTabFrame::displacement() {
return {5, 33};
}
void pTabFrame::remove(unsigned selection) { void pTabFrame::remove(unsigned selection) {
qtTabFrame->removeTab(selection); qtTabFrame->removeTab(selection);
} }
@@ -20,11 +33,10 @@ void pTabFrame::setEnabled(bool enabled) {
void pTabFrame::setGeometry(Geometry geometry) { void pTabFrame::setGeometry(Geometry geometry) {
pWidget::setGeometry(geometry); pWidget::setGeometry(geometry);
geometry.x += 1, geometry.width -= 2; geometry.x += 0, geometry.width -= 5;
geometry.y += 29, geometry.height -= 30; geometry.y += 29, geometry.height -= 33;
for(auto& layout : tabFrame.state.layout) { for(auto& layout : tabFrame.state.layout) {
if(layout == nullptr) continue; if(layout) layout->setGeometry(geometry);
layout->setGeometry(geometry);
} }
synchronizeLayout(); synchronizeLayout();
} }

View File

@@ -1,5 +1,9 @@
namespace phoenix { namespace phoenix {
QWidget* pWidget::container(Widget& widget) {
return nullptr;
}
bool pWidget::focused() { bool pWidget::focused() {
return qtWidget->hasFocus(); return qtWidget->hasFocus();
} }
@@ -24,6 +28,10 @@ void pWidget::setFont(string font) {
} }
void pWidget::setGeometry(Geometry geometry) { void pWidget::setGeometry(Geometry geometry) {
Position displacement = GetDisplacement(&widget);
geometry.x -= displacement.x;
geometry.y -= displacement.y;
qtWidget->setGeometry(geometry.x, geometry.y, geometry.width, geometry.height); qtWidget->setGeometry(geometry.x, geometry.y, geometry.width, geometry.height);
if(widget.onSize) widget.onSize(); if(widget.onSize) widget.onSize();
} }

View File

@@ -1,11 +1,5 @@
namespace phoenix { namespace phoenix {
Window& pWindow::none() {
static Window* window = nullptr;
if(window == nullptr) window = new Window;
return *window;
}
void pWindow::append(Layout& layout) { void pWindow::append(Layout& layout) {
Geometry geometry = window.state.geometry; Geometry geometry = window.state.geometry;
geometry.x = geometry.y = 0; geometry.x = geometry.y = 0;
@@ -14,7 +8,7 @@ void pWindow::append(Layout& layout) {
void pWindow::append(Menu& menu) { void pWindow::append(Menu& menu) {
if(window.state.menuFont != "") menu.p.setFont(window.state.menuFont); if(window.state.menuFont != "") menu.p.setFont(window.state.menuFont);
else menu.p.setFont("Sans, 8"); else menu.p.setFont(Font::sans(8));
qtMenu->addMenu(menu.p.qtMenu); qtMenu->addMenu(menu.p.qtMenu);
} }
@@ -22,8 +16,12 @@ void pWindow::append(Widget& widget) {
if(widget.font().empty() && !window.state.widgetFont.empty()) { if(widget.font().empty() && !window.state.widgetFont.empty()) {
widget.setFont(window.state.widgetFont); widget.setFont(window.state.widgetFont);
} }
if(widget.font().empty()) widget.p.setFont("Sans, 8"); if(widget.font().empty()) widget.p.setFont(Font::sans(8));
widget.p.qtWidget->setParent(qtContainer); if(GetParentWidget(&widget)) {
widget.p.qtWidget->setParent(GetParentWidget(&widget)->p.container(widget));
} else {
widget.p.qtWidget->setParent(qtContainer);
}
widget.setVisible(widget.visible()); widget.setVisible(widget.visible());
} }
@@ -69,10 +67,11 @@ void pWindow::remove(Widget& widget) {
void pWindow::setBackgroundColor(Color color) { void pWindow::setBackgroundColor(Color color) {
QPalette palette; QPalette palette;
palette.setColor(QPalette::Window, QColor(color.red, color.green, color.blue, color.alpha)); palette.setColor(QPalette::Background, QColor(color.red, color.green, color.blue /*, color.alpha */));
qtContainer->setPalette(palette); qtContainer->setPalette(palette);
qtContainer->setAutoFillBackground(true); qtContainer->setAutoFillBackground(true);
qtWindow->setAttribute(Qt::WA_TranslucentBackground, color.alpha != 255); //translucency results are very unpleasant without a compositor; so disable for now
//qtWindow->setAttribute(Qt::WA_TranslucentBackground, color.alpha != 255);
} }
void pWindow::setDroppable(bool droppable) { void pWindow::setDroppable(bool droppable) {

View File

@@ -213,22 +213,6 @@ static LRESULT CALLBACK Application_windowProc(HWND hwnd, UINT msg, WPARAM wpara
case WM_ERASEBKGND: if(window.p.onEraseBackground()) return true; break; case WM_ERASEBKGND: if(window.p.onEraseBackground()) return true; break;
case WM_ENTERMENULOOP: case WM_ENTERSIZEMOVE: window.p.onModalBegin(); return FALSE; case WM_ENTERMENULOOP: case WM_ENTERSIZEMOVE: window.p.onModalBegin(); return FALSE;
case WM_EXITMENULOOP: case WM_EXITSIZEMOVE: window.p.onModalEnd(); return FALSE; case WM_EXITMENULOOP: case WM_EXITSIZEMOVE: window.p.onModalEnd(); return FALSE;
case WM_CTLCOLORBTN:
case WM_CTLCOLORSTATIC: {
Object* object = (Object*)GetWindowLongPtr((HWND)lparam, GWLP_USERDATA);
if(object == nullptr) break;
if(dynamic_cast<HexEdit*>(object) || dynamic_cast<LineEdit*>(object) || dynamic_cast<TextEdit*>(object)) {
//text edit controls, when disabled, use CTLCOLORSTATIC instead of CTLCOLOREDIT
//override this behavior: we do not want read-only edit controls to use the parent window background color
return DefWindowProc(hwnd, WM_CTLCOLOREDIT, wparam, lparam);
} else if(window.p.brush) {
HDC hdc = (HDC)wparam;
SetBkColor((HDC)wparam, window.p.brushColor);
return (INT_PTR)window.p.brush;
}
break;
}
} }
return Shared_windowProc(DefWindowProc, hwnd, msg, wparam, lparam); return Shared_windowProc(DefWindowProc, hwnd, msg, wparam, lparam);

View File

@@ -14,3 +14,5 @@
#include <shlobj.h> #include <shlobj.h>
#include <nall/windows/registry.hpp> #include <nall/windows/registry.hpp>
#include <nall/windows/utf8.hpp> #include <nall/windows/utf8.hpp>
#define TBS_TRANSPARENTBKGND 0x1000

View File

@@ -375,8 +375,8 @@ struct pFrame : public pWidget {
struct pHexEdit : public pWidget { struct pHexEdit : public pWidget {
HexEdit& hexEdit; HexEdit& hexEdit;
HWND scrollBar; WindowProc windowProc = nullptr;
LRESULT CALLBACK (*windowProc)(HWND, UINT, LPARAM, WPARAM); HWND scrollBar = nullptr;
void setColumns(unsigned columns); void setColumns(unsigned columns);
void setLength(unsigned length); void setLength(unsigned length);

View File

@@ -74,6 +74,19 @@ static unsigned GetWindowZOrder(HWND hwnd) {
return z; return z;
} }
static void ImageList_Append(HIMAGELIST imageList, const nall::image& source, unsigned scale) {
auto image = source;
if(image.empty()) {
image.allocate(scale, scale);
image.fill(GetSysColor(COLOR_WINDOW));
}
image.transform(0, 32, 255u << 24, 255u << 16, 255u << 8, 255u << 0);
image.scale(scale, scale);
HBITMAP bitmap = CreateBitmap(image);
ImageList_Add(imageList, bitmap, NULL);
DeleteObject(bitmap);
}
static Keyboard::Keycode Keysym(unsigned keysym, unsigned keyflags) { static Keyboard::Keycode Keysym(unsigned keysym, unsigned keyflags) {
#define pressed(keysym) (GetAsyncKeyState(keysym) & 0x8000) #define pressed(keysym) (GetAsyncKeyState(keysym) & 0x8000)
#define enabled(keysym) (GetKeyState(keysym)) #define enabled(keysym) (GetKeyState(keysym))
@@ -231,12 +244,34 @@ static LRESULT CALLBACK Shared_windowProc(WindowProc windowProc, HWND hwnd, UINT
if(process == false) return DefWindowProc(hwnd, msg, wparam, lparam); if(process == false) return DefWindowProc(hwnd, msg, wparam, lparam);
switch(msg) { switch(msg) {
case WM_CTLCOLORBTN:
case WM_CTLCOLORSTATIC: {
Object* object = (Object*)GetWindowLongPtr((HWND)lparam, GWLP_USERDATA);
if(object == nullptr) break;
if(dynamic_cast<HexEdit*>(object) || dynamic_cast<LineEdit*>(object) || dynamic_cast<TextEdit*>(object)) {
//text edit controls, when disabled, use CTLCOLORSTATIC instead of CTLCOLOREDIT
//override this behavior: we do not want read-only edit controls to use the parent window background color
return windowProc(hwnd, WM_CTLCOLOREDIT, wparam, lparam);
} else if(!GetParentWidget((Sizable*)object) && window.p.brush) {
SetBkColor((HDC)wparam, window.p.brushColor);
return (INT_PTR)window.p.brush;
}/* else {
//this will repaint the background properly, but the foreground isn't always rendered after ...
RECT rc;
GetClientRect((HWND)lparam, &rc);
DrawThemeParentBackground((HWND)lparam, (HDC)wparam, &rc);
SetBkMode((HDC)wparam, TRANSPARENT);
return (INT_PTR)GetStockBrush(HOLLOW_BRUSH);
}*/
break;
}
case WM_DRAWITEM: { case WM_DRAWITEM: {
unsigned id = LOWORD(wparam); unsigned id = LOWORD(wparam);
HWND control = GetDlgItem(hwnd, id); HWND control = GetDlgItem(hwnd, id);
Object* object = (Object*)GetWindowLongPtr(control, GWLP_USERDATA); Object* object = (Object*)GetWindowLongPtr(control, GWLP_USERDATA);
if(object == nullptr) break; if(object == nullptr) break;
if(dynamic_cast<TabFrame*>(object)) { ((TabFrame*)object)->p.onDrawItem(lparam); break; } if(dynamic_cast<TabFrame*>(object)) { ((TabFrame*)object)->p.onDrawItem(lparam); return TRUE; }
break; break;
} }
@@ -245,16 +280,16 @@ static LRESULT CALLBACK Shared_windowProc(WindowProc windowProc, HWND hwnd, UINT
HWND control = GetDlgItem(hwnd, id); HWND control = GetDlgItem(hwnd, id);
Object* object = control ? (Object*)GetWindowLongPtr(control, GWLP_USERDATA) : (Object*)(&pObject::find(id)->object); Object* object = control ? (Object*)GetWindowLongPtr(control, GWLP_USERDATA) : (Object*)(&pObject::find(id)->object);
if(object == nullptr) break; if(object == nullptr) break;
if(dynamic_cast<Item*>(object)) { ((Item*)object)->p.onActivate(); break; } if(dynamic_cast<Item*>(object)) { ((Item*)object)->p.onActivate(); return FALSE; }
if(dynamic_cast<CheckItem*>(object)) { ((CheckItem*)object)->p.onToggle(); break; } if(dynamic_cast<CheckItem*>(object)) { ((CheckItem*)object)->p.onToggle(); return FALSE; }
if(dynamic_cast<Button*>(object)) { ((Button*)object)->p.onActivate(); break; } if(dynamic_cast<Button*>(object)) { ((Button*)object)->p.onActivate(); return FALSE; }
if(dynamic_cast<CheckButton*>(object)) { ((CheckButton*)object)->p.onToggle(); break; } if(dynamic_cast<CheckButton*>(object)) { ((CheckButton*)object)->p.onToggle(); return FALSE; }
if(dynamic_cast<CheckLabel*>(object)) { ((CheckLabel*)object)->p.onToggle(); break; } if(dynamic_cast<CheckLabel*>(object)) { ((CheckLabel*)object)->p.onToggle(); return FALSE; }
if(dynamic_cast<ComboButton*>(object) && HIWORD(wparam) == CBN_SELCHANGE) { ((ComboButton*)object)->p.onChange(); break; } if(dynamic_cast<ComboButton*>(object) && HIWORD(wparam) == CBN_SELCHANGE) { ((ComboButton*)object)->p.onChange(); return FALSE; }
if(dynamic_cast<LineEdit*>(object) && HIWORD(wparam) == EN_CHANGE) { ((LineEdit*)object)->p.onChange(); break; } if(dynamic_cast<LineEdit*>(object) && HIWORD(wparam) == EN_CHANGE) { ((LineEdit*)object)->p.onChange(); return FALSE; }
if(dynamic_cast<RadioButton*>(object)) { ((RadioButton*)object)->p.onActivate(); break; } if(dynamic_cast<RadioButton*>(object)) { ((RadioButton*)object)->p.onActivate(); return FALSE; }
if(dynamic_cast<RadioLabel*>(object)) { ((RadioLabel*)object)->p.onActivate(); break; } if(dynamic_cast<RadioLabel*>(object)) { ((RadioLabel*)object)->p.onActivate(); return FALSE; }
if(dynamic_cast<TextEdit*>(object) && HIWORD(wparam) == EN_CHANGE) { ((TextEdit*)object)->p.onChange(); break; } if(dynamic_cast<TextEdit*>(object) && HIWORD(wparam) == EN_CHANGE) { ((TextEdit*)object)->p.onChange(); return FALSE; }
break; break;
} }
@@ -283,8 +318,8 @@ static LRESULT CALLBACK Shared_windowProc(WindowProc windowProc, HWND hwnd, UINT
if(object == nullptr) break; if(object == nullptr) break;
if(dynamic_cast<HorizontalScroller*>(object)) { ((HorizontalScroller*)object)->p.onChange(wparam); return TRUE; } if(dynamic_cast<HorizontalScroller*>(object)) { ((HorizontalScroller*)object)->p.onChange(wparam); return TRUE; }
if(dynamic_cast<VerticalScroller*>(object)) { ((VerticalScroller*)object)->p.onChange(wparam); return TRUE; } if(dynamic_cast<VerticalScroller*>(object)) { ((VerticalScroller*)object)->p.onChange(wparam); return TRUE; }
if(dynamic_cast<HorizontalSlider*>(object)) { ((HorizontalSlider*)object)->p.onChange(); break; } if(dynamic_cast<HorizontalSlider*>(object)) { ((HorizontalSlider*)object)->p.onChange(); return TRUE; }
if(dynamic_cast<VerticalSlider*>(object)) { ((VerticalSlider*)object)->p.onChange(); break; } if(dynamic_cast<VerticalSlider*>(object)) { ((VerticalSlider*)object)->p.onChange(); return TRUE; }
break; break;
} }
} }

View File

@@ -24,7 +24,7 @@ void pComboButton::reset() {
} }
void pComboButton::setGeometry(Geometry geometry) { void pComboButton::setGeometry(Geometry geometry) {
SetWindowPos(hwnd, NULL, geometry.x, geometry.y, geometry.width, 1, SWP_NOZORDER); pWidget::setGeometry(geometry);
RECT rc; RECT rc;
GetWindowRect(hwnd, &rc); GetWindowRect(hwnd, &rc);
unsigned adjustedHeight = geometry.height - ((rc.bottom - rc.top) - SendMessage(hwnd, CB_GETITEMHEIGHT, (WPARAM)-1, 0)); unsigned adjustedHeight = geometry.height - ((rc.bottom - rc.top) - SendMessage(hwnd, CB_GETITEMHEIGHT, (WPARAM)-1, 0));

View File

@@ -4,7 +4,6 @@ static LRESULT CALLBACK HexEdit_windowProc(HWND hwnd, UINT msg, WPARAM wparam, L
HexEdit& hexEdit = *(HexEdit*)GetWindowLongPtr(hwnd, GWLP_USERDATA); HexEdit& hexEdit = *(HexEdit*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
switch(msg) { switch(msg) {
case WM_KEYDOWN: case WM_KEYDOWN:
if(hexEdit.p.keyPress(wparam)) return 0; if(hexEdit.p.keyPress(wparam)) return 0;
break; break;
@@ -45,7 +44,6 @@ static LRESULT CALLBACK HexEdit_windowProc(HWND hwnd, UINT msg, WPARAM wparam, L
hexEdit.p.scrollTo(info.nPos); hexEdit.p.scrollTo(info.nPos);
return TRUE; return TRUE;
} }
} }
return hexEdit.p.windowProc(hwnd, msg, wparam, lparam); return hexEdit.p.windowProc(hwnd, msg, wparam, lparam);
@@ -123,7 +121,7 @@ void pHexEdit::constructor() {
); );
SetWindowLongPtr(scrollBar, GWLP_USERDATA, (LONG_PTR)&hexEdit); SetWindowLongPtr(scrollBar, GWLP_USERDATA, (LONG_PTR)&hexEdit);
windowProc = (LRESULT CALLBACK (*)(HWND, UINT, LPARAM, WPARAM))GetWindowLongPtr(hwnd, GWLP_WNDPROC); windowProc = (WindowProc)GetWindowLongPtr(hwnd, GWLP_WNDPROC);
SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)HexEdit_windowProc); SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)HexEdit_windowProc);
setDefaultFont(); setDefaultFont();

View File

@@ -17,10 +17,11 @@ void pHorizontalSlider::setPosition(unsigned position) {
void pHorizontalSlider::constructor() { void pHorizontalSlider::constructor() {
hwnd = CreateWindow( hwnd = CreateWindow(
TRACKBAR_CLASS, L"", WS_CHILD | WS_TABSTOP | TBS_NOTICKS | TBS_BOTH | TBS_HORZ, TRACKBAR_CLASS, L"", WS_CHILD | WS_TABSTOP | TBS_TRANSPARENTBKGND | TBS_NOTICKS | TBS_BOTH | TBS_HORZ,
0, 0, 0, 0, parentHwnd, (HMENU)id, GetModuleHandle(0), 0 0, 0, 0, 0, parentHwnd, (HMENU)id, GetModuleHandle(0), 0
); );
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&horizontalSlider); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&horizontalSlider);
unsigned position = horizontalSlider.state.position; unsigned position = horizontalSlider.state.position;
setLength(horizontalSlider.state.length); setLength(horizontalSlider.state.length);
horizontalSlider.setPosition(position); horizontalSlider.setPosition(position);

View File

@@ -36,19 +36,13 @@ static LRESULT CALLBACK Label_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPA
Window* window = (Window*)label->Sizable::state.window; Window* window = (Window*)label->Sizable::state.window;
if(window == nullptr) return DefWindowProc(hwnd, msg, wparam, lparam); if(window == nullptr) return DefWindowProc(hwnd, msg, wparam, lparam);
if(msg == WM_GETDLGCODE) { switch(msg) {
return DLGC_STATIC | DLGC_WANTCHARS; case WM_GETDLGCODE: return DLGC_STATIC | DLGC_WANTCHARS;
} case WM_ERASEBKGND: return TRUE;
case WM_PAINT: {
if(msg == WM_ERASEBKGND) {
//background is erased during WM_PAINT to prevent flickering
return TRUE;
}
if(msg == WM_PAINT) {
PAINTSTRUCT ps; PAINTSTRUCT ps;
RECT rc;
BeginPaint(hwnd, &ps); BeginPaint(hwnd, &ps);
RECT rc;
GetClientRect(hwnd, &rc); GetClientRect(hwnd, &rc);
DrawThemeParentBackground(hwnd, ps.hdc, &rc); DrawThemeParentBackground(hwnd, ps.hdc, &rc);
SetBkMode(ps.hdc, TRANSPARENT); SetBkMode(ps.hdc, TRANSPARENT);
@@ -64,6 +58,8 @@ static LRESULT CALLBACK Label_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPA
rc.bottom = rc.top + height; rc.bottom = rc.top + height;
DrawText(ps.hdc, text, -1, &rc, DT_LEFT | DT_END_ELLIPSIS); DrawText(ps.hdc, text, -1, &rc, DT_LEFT | DT_END_ELLIPSIS);
EndPaint(hwnd, &ps); EndPaint(hwnd, &ps);
return FALSE;
}
} }
return DefWindowProc(hwnd, msg, wparam, lparam); return DefWindowProc(hwnd, msg, wparam, lparam);

View File

@@ -23,19 +23,6 @@ void ListView_SetImage(HWND hwnd, HIMAGELIST imageList, unsigned row, unsigned c
ListView_SetItem(hwnd, &item); ListView_SetItem(hwnd, &item);
} }
void ImageList_Append(HIMAGELIST imageList, const nall::image& source) {
auto image = source;
if(image.empty()) {
image.allocate(15, 15);
image.fill(GetSysColor(COLOR_WINDOW));
}
image.transform(0, 32, 255u << 24, 255u << 16, 255u << 8, 255u << 0);
image.scale(15, 15, Interpolation::Linear);
HBITMAP bitmap = CreateBitmap(image);
ImageList_Add(imageList, bitmap, NULL);
DeleteObject(bitmap);
}
void pListView::append(const lstring& list) { void pListView::append(const lstring& list) {
wchar_t empty[] = L""; wchar_t empty[] = L"";
unsigned row = ListView_GetItemCount(hwnd); unsigned row = ListView_GetItemCount(hwnd);
@@ -122,7 +109,7 @@ void pListView::setImage(unsigned selection, unsigned position, const image& ima
//append and assign new image //append and assign new image
imageMap(selection)(position) = images.size(); imageMap(selection)(position) = images.size();
images.append(image); images.append(image);
ImageList_Append(imageList, image); ImageList_Append(imageList, image, 15);
ListView_SetImage(hwnd, imageList, selection, position, imageMap(selection)(position)); ListView_SetImage(hwnd, imageList, selection, position, imageMap(selection)(position));
} }
@@ -211,7 +198,7 @@ void pListView::buildImageList() {
} }
//build image list //build image list
for(auto& imageItem : images) ImageList_Append(imageList, imageItem); for(auto& imageItem : images) ImageList_Append(imageList, imageItem, 15);
if(images.size() <= 1) return; if(images.size() <= 1) return;
//set images for all cells //set images for all cells

View File

@@ -94,7 +94,7 @@ void pTabFrame::buildImageList() {
unsigned size = pFont::size(hfont, " ").height; unsigned size = pFont::size(hfont, " ").height;
imageList = ImageList_Create(size, size, ILC_COLOR32, 1, 0); imageList = ImageList_Create(size, size, ILC_COLOR32, 1, 0);
for(auto& image : tabFrame.state.image) { for(auto& image : tabFrame.state.image) {
ImageList_Append(imageList, image); ImageList_Append(imageList, image, size);
} }
TabCtrl_SetImageList(hwnd, imageList); TabCtrl_SetImageList(hwnd, imageList);
for(unsigned n = 0; n < tabFrame.state.image.size(); n++) { for(unsigned n = 0; n < tabFrame.state.image.size(); n++) {

View File

@@ -17,10 +17,11 @@ void pVerticalSlider::setPosition(unsigned position) {
void pVerticalSlider::constructor() { void pVerticalSlider::constructor() {
hwnd = CreateWindow( hwnd = CreateWindow(
TRACKBAR_CLASS, L"", WS_CHILD | WS_TABSTOP | TBS_NOTICKS | TBS_BOTH | TBS_VERT, TRACKBAR_CLASS, L"", WS_CHILD | WS_TABSTOP | TBS_TRANSPARENTBKGND | TBS_NOTICKS | TBS_BOTH | TBS_VERT,
0, 0, 0, 0, parentHwnd, (HMENU)id, GetModuleHandle(0), 0 0, 0, 0, 0, parentHwnd, (HMENU)id, GetModuleHandle(0), 0
); );
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&verticalSlider); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&verticalSlider);
unsigned position = verticalSlider.state.position; unsigned position = verticalSlider.state.position;
setLength(verticalSlider.state.length); setLength(verticalSlider.state.length);
verticalSlider.setPosition(position); verticalSlider.setPosition(position);

View File

@@ -41,6 +41,10 @@ ConfigurationSettings::ConfigurationSettings() {
server.append(server.password = "", "Password"); server.append(server.password = "", "Password");
append(server, "Server"); append(server, "Server");
library.append(library.selection = 0, "Selection");
library.append(library.showOnStartup = true, "ShowOnStartup");
append(library, "Library");
load(); load();
} }

View File

@@ -45,6 +45,11 @@ struct ConfigurationSettings : Configuration::Document {
string password; string password;
} server; } server;
struct Library : Configuration::Node {
unsigned selection;
bool showOnStartup;
} library;
void load(); void load();
void save(); void save();
ConfigurationSettings(); ConfigurationSettings();

View File

@@ -66,7 +66,7 @@ Program::Program(int argc, char** argv) {
utility = new Utility; utility = new Utility;
inputManager = new InputManager; inputManager = new InputManager;
windowManager = new WindowManager; windowManager = new WindowManager;
browser = new Browser; libraryManager = new LibraryManager;
presentation = new Presentation; presentation = new Presentation;
dipSwitches = new DipSwitches; dipSwitches = new DipSwitches;
videoSettings = new VideoSettings; videoSettings = new VideoSettings;
@@ -85,6 +85,8 @@ Program::Program(int argc, char** argv) {
presentation->setVisible(); presentation->setVisible();
utility->resize(); utility->resize();
if(config->library.showOnStartup) libraryManager->setVisible();
video.set(Video::Handle, presentation->viewport.handle()); video.set(Video::Handle, presentation->viewport.handle());
if(!video.cap(Video::Depth) || !video.set(Video::Depth, depth = 30u)) { if(!video.cap(Video::Depth) || !video.set(Video::Depth, depth = 30u)) {
video.set(Video::Depth, depth = 24u); video.set(Video::Depth, depth = 24u);
@@ -114,7 +116,6 @@ Program::Program(int argc, char** argv) {
utility->unload(); utility->unload();
config->save(); config->save();
browser->saveConfiguration();
inputManager->saveConfiguration(); inputManager->saveConfiguration();
windowManager->saveGeometry(); windowManager->saveGeometry();

View File

@@ -1,178 +0,0 @@
Browser* browser = nullptr;
Browser::Browser() {
bootstrap();
setGeometry({128, 128, 640, 400});
windowManager->append(this, "Browser");
layout.setMargin(5);
homeButton.setImage({resource::home, sizeof resource::home});
upButton.setImage({resource::up, sizeof resource::up});
openButton.setText("Open");
append(layout);
layout.append(pathLayout, {~0, 0}, 5);
pathLayout.append(pathEdit, {~0, 0}, 5);
pathLayout.append(homeButton, {28, 28}, 5);
pathLayout.append(upButton, {28, 28});
layout.append(fileList, {~0, ~0}, 5);
layout.append(controlLayout, {~0, 0});
controlLayout.append(filterLabel, {~0, 0}, 5);
controlLayout.append(openButton, {80, 0});
pathEdit.onActivate = [&] {
string path = pathEdit.text();
path.transform("\\", "/");
if(path.endswith("/") == false) path.append("/");
setPath(path);
};
homeButton.onActivate = [&] {
string libraryPath = string::read({configpath(), "higan/library.bml"}).strip().ltrim<1>("Path: ").transform("\\", "/");
if(libraryPath.empty()) libraryPath = {userpath(), "Emulation/"};
if(libraryPath.endswith("/") == false) libraryPath.append("/");
setPath(libraryPath);
};
upButton.onActivate = [&] {
setPath(parentdir(path));
};
fileList.onChange = {&Browser::synchronize, this};
fileList.onActivate = openButton.onActivate = {&Browser::fileListActivate, this};
onClose = [&] {
setModal(false);
setVisible(false);
};
synchronize();
}
void Browser::synchronize() {
openButton.setEnabled(fileList.selected());
if(fileList.selected()) {
for(auto& folder : folderList) {
if(folder.extension == extension) {
folder.selection = fileList.selection();
}
}
}
}
void Browser::saveConfiguration() {
config.save(program->path("paths.bml"));
}
void Browser::bootstrap() {
for(auto& emulator : program->emulator) {
for(auto& media : emulator->media) {
bool found = false;
for(auto& folder : folderList) {
if(folder.extension == media.type) {
found = true;
break;
}
}
if(found == true) continue;
Folder folder;
folder.extension = media.type;
folder.path = {userpath(), "Emulation/", media.name, "/"};
folder.selection = 0;
folderList.append(folder);
}
}
for(auto& folder : folderList) {
Configuration::Node node;
node.append(folder.path, "Path");
node.append(folder.selection, "Selection");
config.append(node, folder.extension);
}
config.load(program->path("paths.bml"));
config.save(program->path("paths.bml"));
}
string Browser::select(string title, string extension) {
this->extension = extension;
string path;
unsigned selection = 0;
for(auto& folder : folderList) {
if(folder.extension == extension) {
path = folder.path;
selection = folder.selection;
break;
}
}
if(path.empty()) path = program->basepath;
setPath(path, selection);
filterLabel.setText({"Filter: *.", extension});
audio.clear();
setTitle(title);
setVisible(true);
fileList.setFocused();
outputFilename = "";
setModal();
return outputFilename;
}
void Browser::setPath(string path, unsigned selection) {
//save path for next browser selection
for(auto& folder : folderList) {
if(folder.extension == extension) folder.path = path;
}
this->path = path;
pathEdit.setText(path);
fileList.reset();
filenameList.reset();
lstring contents = directory::ifolders(path);
for(auto& filename : contents) {
string suffix = {".", this->extension, "/"};
if(filename.endswith("/") && !filename.endswith(suffix)) {
string name = filename;
name.rtrim<1>("/");
fileList.append(name);
fileList.setImage(filenameList.size(), 0, {resource::folder, sizeof resource::folder});
filenameList.append(filename);
}
}
for(auto& filename : contents) {
string suffix = {".", this->extension, "/"};
if(filename.endswith(suffix)) {
string name = filename;
name.rtrim<1>(suffix);
fileList.append(name);
if(1 || file::exists({path, filename, "unverified"}) == false) {
fileList.setImage(filenameList.size(), 0, {resource::game, sizeof resource::game});
} else {
//disabled for now due to performance penalty
fileList.setImage(filenameList.size(), 0, {resource::unverified, sizeof resource::unverified});
}
filenameList.append(filename);
}
}
fileList.setSelection(selection);
fileList.setFocused();
synchronize();
}
void Browser::fileListActivate() {
unsigned selection = fileList.selection();
string filename = filenameList[selection];
if(string{filename}.rtrim<1>("/").endswith(this->extension) == false) return setPath({path, filename});
outputFilename = {path, filename};
onClose();
}

View File

@@ -1,37 +0,0 @@
struct Browser : Window {
VerticalLayout layout;
HorizontalLayout pathLayout;
LineEdit pathEdit;
Button homeButton;
Button upButton;
ListView fileList;
HorizontalLayout controlLayout;
Label filterLabel;
Button openButton;
string select(string title, string extension);
void saveConfiguration();
void synchronize();
void bootstrap();
Browser();
private:
Configuration::Document config;
struct Folder {
string extension;
string path;
unsigned selection;
};
vector<Folder> folderList;
string outputFilename;
string extension;
string path;
lstring filenameList;
void setPath(string path, unsigned selection = 0);
void fileListActivate();
};
extern Browser* browser;

View File

@@ -17,6 +17,7 @@ DipSwitches::DipSwitches() {
controlLayout.append(accept, {80, 0}); controlLayout.append(accept, {80, 0});
setGeometry({128, 128, 250, layout.minimumSize().height}); setGeometry({128, 128, 250, layout.minimumSize().height});
windowManager->append(this, "DipSwitches");
onClose = accept.onActivate = [&] { onClose = accept.onActivate = [&] {
setModal(false); setModal(false);

View File

@@ -1,4 +1,4 @@
#include "../ethos.hpp" #include "../ethos.hpp"
#include "browser.cpp" #include "library.cpp"
#include "presentation.cpp" #include "presentation.cpp"
#include "dip-switches.cpp" #include "dip-switches.cpp"

View File

@@ -1,3 +1,3 @@
#include "browser.hpp" #include "library.hpp"
#include "presentation.hpp" #include "presentation.hpp"
#include "dip-switches.hpp" #include "dip-switches.hpp"

View File

@@ -0,0 +1,185 @@
LibraryManager* libraryManager = nullptr;
LibraryBrowser::LibraryBrowser() {
setMargin(5);
informationType.setText({
"Title:\n",
"Revision:\n",
"Region:\n",
"Serial:"
});
append(folders, {~0, ~0}, 5);
append(informationLayout, {~0, Font::size(program->normalFont, " ").height * 4});
informationLayout.append(informationType, {0, ~0}, 5);
informationLayout.append(information, {~0, ~0});
folders.onActivate = {&LibraryBrowser::onActivate, this};
folders.onChange = {&LibraryBrowser::setInformation, this};
}
void LibraryBrowser::onActivate() {
if(folders.selected() == false) return;
unsigned selection = folders.selection();
string pathname = {this->pathname, folders.text(selection, 0), filterSuffix};
if(libraryManager->slotLoad == false) {
libraryManager->setStatusText(folders.text(selection, 0));
utility->loadMedia(pathname);
} else {
libraryManager->setStatusText({libraryManager->statusText(), " + ", folders.text(selection, 0)});
libraryManager->setModal(false);
libraryManager->loadPathname = pathname;
}
}
void LibraryBrowser::refresh() {
folders.reset();
lstring pathnames = directory::ifolders(pathname, filterMask);
unsigned selection = 0;
for(auto& pathname : pathnames) {
folders.append(string{pathname}.rtrim<1>(filterSuffix));
folders.setImage(selection++, 0, {resource::game, sizeof resource::game});
}
}
void LibraryBrowser::setFilter(const string& filter) {
this->filter = filter;
filterMask = {"*.", filter};
filterSuffix = {".", filter, "/"};
}
void LibraryBrowser::setInformation() {
if(folders.selected() == false) {
information.setText("");
} else {
string manifest = {pathname, folders.text(folders.selection(), 0), filterSuffix, "manifest.bml"};
auto document = Markup::Document(file::read(manifest));
information.setText({
document["information/title"].text(), "\n",
document["information/revision"].text(), "\n",
document["information/region"].text(), "\n",
document["information/serial"].text(), "\n"
});
}
}
void LibraryBrowser::setPath(const string& pathname) {
this->pathname = pathname;
refresh();
}
LibraryManager::LibraryManager() {
setTitle("Game Library");
setStatusVisible();
setGeometry({128, 128, 960, 640});
windowManager->append(this, "LibraryManager");
layout.setMargin(5);
libraryFrame.append("Import Games");
importLayout.setMargin(5);
importInformation.setText({
"higan manages games in a library. To play a game, you must first import the game.\n"
"After doing so, the game will appear inside your library, and can then be loaded and played."
});
importButton.setText("Import Game ...");
libraryFrame.setLayout(0, importLayout);
bootstrap();
libraryFrame.setSelection(config->library.selection);
append(layout);
layout.append(libraryFrame, {~0, ~0});
importLayout.append(importInformation, {0, 0}, 5);
importLayout.append(importButton, {0, 0});
onClose = [&] {
setModal(false);
setVisible(false);
};
libraryFrame.onChange = [&] {
config->library.selection = libraryFrame.selection();
};
importButton.onActivate = [&] {
if(program->ananke.open() == false) {
MessageWindow().setText("ananke must be installed to use this feature").warning();
return;
}
function<string ()> browse = program->ananke.sym("ananke_browse");
if(!browse) return;
string pathname = browse();
if(pathname.empty()) return;
MessageWindow().setText({"Successfully imported ", notdir(pathname.rtrim<1>("/"))}).information();
string type = extension(pathname);
unsigned selection = 1;
for(auto& browser : browsers) {
if(browser->filter == type) {
browser->refresh();
libraryFrame.setSelection(selection);
break;
}
selection++;
}
};
}
void LibraryManager::bootstrap() {
unsigned selection = 1;
string basepath = utility->libraryPath();
vector<string> names;
for(auto& emulator : program->emulator) {
for(auto& media : emulator->media) {
if(media.bootable == false) continue;
if(names.find(media.name)) continue;
names.append(media.name);
LibraryBrowser* browser = new LibraryBrowser;
browser->setFilter(media.type);
browser->setPath({basepath, media.name, "/"});
libraryFrame.append(media.name);
libraryFrame.setLayout(selection++, *browser);
browsers.append(browser);
}
}
for(auto& emulator : program->emulator) {
for(auto& media : emulator->media) {
if(media.bootable == true) continue;
if(names.find(media.name)) continue;
names.append(media.name);
LibraryBrowser* browser = new LibraryBrowser;
browser->setFilter(media.type);
browser->setPath({basepath, media.name, "/"});
libraryFrame.append(media.name);
libraryFrame.setLayout(selection++, *browser);
browsers.append(browser);
}
}
}
string LibraryManager::load(const string& type) {
setFocused();
unsigned selection = 1;
for(auto& browser : browsers) {
if(browser->filter == type) {
libraryFrame.setSelection(selection);
break;
}
selection++;
}
slotLoad = true;
loadPathname = "";
setModal(true);
slotLoad = false;
return loadPathname;
}
void LibraryManager::setVisible(bool visible) {
setStatusText("");
Window::setVisible(visible);
}

View File

@@ -0,0 +1,37 @@
struct LibraryBrowser : VerticalLayout {
ListView folders;
HorizontalLayout informationLayout;
Label informationType;
Label information;
LibraryBrowser();
void onActivate();
void refresh();
void setFilter(const string& filter);
void setInformation();
void setPath(const string& pathname);
string filter;
string filterMask;
string filterSuffix;
string pathname;
};
struct LibraryManager : Window {
VerticalLayout layout;
TabFrame libraryFrame;
VerticalLayout importLayout;
Label importInformation;
Button importButton;
vector<LibraryBrowser*> browsers;
LibraryManager();
void bootstrap();
string load(const string& type);
void setVisible(bool visible = true);
bool slotLoad = false;
string loadPathname;
};
extern LibraryManager* libraryManager;

View File

@@ -62,7 +62,7 @@ Presentation::Presentation() {
viewport.setDroppable(); viewport.setDroppable();
loadMenu.setText("Library"); loadMenu.setText("Library");
loadImport.setText("Import Game ..."); loadGame.setText("Load Game ...");
settingsMenu.setText("Settings"); settingsMenu.setText("Settings");
videoMenu.setText("Video"); videoMenu.setText("Video");
centerVideo.setText("Center"); centerVideo.setText("Center");
@@ -90,11 +90,7 @@ Presentation::Presentation() {
synchronizeTime.setText("Synchronize Time"); synchronizeTime.setText("Synchronize Time");
append(loadMenu); append(loadMenu);
for(auto& item : loadListSystem) loadMenu.append(*item); loadMenu.append(loadGame);
if(program->ananke.open()) {
loadMenu.append(loadSeparator);
loadMenu.append(loadImport);
}
for(auto& systemItem : emulatorList) append(systemItem->menu); for(auto& systemItem : emulatorList) append(systemItem->menu);
append(settingsMenu); append(settingsMenu);
settingsMenu.append(videoMenu); settingsMenu.append(videoMenu);
@@ -154,15 +150,7 @@ Presentation::Presentation() {
} }
}; };
loadImport.onActivate = [&] { loadGame.onActivate = [&] { libraryManager->setVisible(); };
if(program->ananke.open() == false) return;
function<string ()> browse = program->ananke.sym("ananke_browse");
if(!browse) return;
string pathname = browse();
if(pathname.empty()) return;
utility->loadMedia(pathname);
};
shaderNone.onActivate = [&] { config->video.shader = "None"; utility->updateShader(); }; shaderNone.onActivate = [&] { config->video.shader = "None"; utility->updateShader(); };
shaderBlur.onActivate = [&] { config->video.shader = "Blur"; utility->updateShader(); }; shaderBlur.onActivate = [&] { config->video.shader = "Blur"; utility->updateShader(); };
shaderEmulation.onActivate = [&] { config->video.shader = "Display Emulation"; utility->updateShader(); }; shaderEmulation.onActivate = [&] { config->video.shader = "Display Emulation"; utility->updateShader(); };
@@ -190,16 +178,6 @@ void Presentation::bootstrap() {
auto iEmulator = new Emulator; auto iEmulator = new Emulator;
iEmulator->interface = emulator; iEmulator->interface = emulator;
for(auto& media : emulator->media) {
if(media.bootable == false) continue;
auto item = new Item;
item->onActivate = [=, &media] {
utility->loadMedia(iEmulator->interface, media);
};
item->setText({media.name, " ..."});
loadListSystem.append(item);
}
iEmulator->menu.setText(emulator->information.name); iEmulator->menu.setText(emulator->information.name);
iEmulator->power.setText("Power"); iEmulator->power.setText("Power");
iEmulator->reset.setText("Reset"); iEmulator->reset.setText("Reset");

View File

@@ -22,9 +22,7 @@ struct Presentation : Window {
Emulator* active = nullptr; Emulator* active = nullptr;
Menu loadMenu; Menu loadMenu;
vector<Item*> loadListSystem; Item loadGame;
Separator loadSeparator;
Item loadImport;
Menu settingsMenu; Menu settingsMenu;
Menu videoMenu; Menu videoMenu;
RadioItem centerVideo; RadioItem centerVideo;

View File

@@ -2,19 +2,19 @@ AdvancedSettings* advancedSettings = nullptr;
AdvancedSettings::AdvancedSettings() { AdvancedSettings::AdvancedSettings() {
driverTitle.setFont(program->boldFont); driverTitle.setFont(program->boldFont);
driverTitle.setText("Driver Selection"); driverTitle.setText("Driver Selection:");
videoLabel.setText("Video:"); videoLabel.setText("Video:");
audioLabel.setText("Audio:"); audioLabel.setText("Audio:");
inputLabel.setText("Input:"); inputLabel.setText("Input:");
libraryTitle.setFont(program->boldFont); libraryTitle.setFont(program->boldFont);
libraryTitle.setText("Game Library Path"); libraryTitle.setText("Game Library:");
libraryLabel.setText("Path:"); libraryLabel.setText("Path:");
libraryPath.setEditable(false); libraryPath.setEditable(false);
string path = string::read({configpath(), "higan/library.bml"}).strip().ltrim<1>("Path: ").transform("\\", "/"); libraryPath.setText(utility->libraryPath());
if(path.empty()) path = {userpath(), "Emulation/"};
if(path.endswith("/") == false) path.append("/");
libraryPath.setText(path);
libraryBrowse.setText("Browse ..."); libraryBrowse.setText("Browse ...");
libraryShowOnStartup.setChecked(config->library.showOnStartup);
libraryShowOnStartup.setText("Show game library on program start");
information.setText("Note: changing advanced settings requires program restart to take effect.");
infoLabel.setFont(program->boldFont); infoLabel.setFont(program->boldFont);
infoLabel.setText({ infoLabel.setText({
Emulator::Name, " v", Emulator::Version, "\n", Emulator::Name, " v", Emulator::Version, "\n",
@@ -53,10 +53,12 @@ AdvancedSettings::AdvancedSettings() {
driverLayout.append(inputLabel, {0, 0}, 5); driverLayout.append(inputLabel, {0, 0}, 5);
driverLayout.append(inputDriver, {~0, 0}); driverLayout.append(inputDriver, {~0, 0});
append(libraryTitle, {~0, 0}); append(libraryTitle, {~0, 0});
append(libraryLayout, {~0, 0}, 15); append(libraryLayout, {~0, 0});
libraryLayout.append(libraryLabel, {0, 0}, 5); libraryLayout.append(libraryLabel, {0, 0}, 5);
libraryLayout.append(libraryPath, {~0, 0}, 5); libraryLayout.append(libraryPath, {~0, 0}, 5);
libraryLayout.append(libraryBrowse, {80, 0}); libraryLayout.append(libraryBrowse, {80, 0});
append(libraryShowOnStartup, {~0, 0}, 15);
append(information, {~0, 0}, 15);
if(Intrinsics::platform() != Intrinsics::Platform::MacOSX) { if(Intrinsics::platform() != Intrinsics::Platform::MacOSX) {
append(spacer, {~0, ~0}); append(spacer, {~0, ~0});
append(infoLabel, {~0, 0}); append(infoLabel, {~0, 0});
@@ -72,4 +74,8 @@ AdvancedSettings::AdvancedSettings() {
file::write({configpath(), "higan/library.bml"}, {"Path: ", path, "\n"}); file::write({configpath(), "higan/library.bml"}, {"Path: ", path, "\n"});
libraryPath.setText(path); libraryPath.setText(path);
}; };
libraryShowOnStartup.onToggle = [&] {
config->library.showOnStartup = libraryShowOnStartup.checked();
};
} }

View File

@@ -13,6 +13,9 @@ struct AdvancedSettings : SettingsLayout {
Label libraryLabel; Label libraryLabel;
LineEdit libraryPath; LineEdit libraryPath;
Button libraryBrowse; Button libraryBrowse;
CheckLabel libraryShowOnStartup;
Label information;
Widget spacer; Widget spacer;
Label infoLabel; Label infoLabel;

View File

@@ -12,16 +12,6 @@ void Utility::loadMedia(string pathname) {
pathname.transform("\\", "/"); pathname.transform("\\", "/");
if(pathname.endswith("/")) pathname.rtrim("/"); if(pathname.endswith("/")) pathname.rtrim("/");
//if a filename was provided: convert to game folder and then load
if(!directory::exists(pathname) && file::exists(pathname)) {
if(program->ananke.open() == false) return;
function<string (string)> open = program->ananke.sym("ananke_open");
if(!open) return;
string name = open(pathname);
if(name.empty()) return;
return loadMedia(name);
}
if(!directory::exists(pathname)) return; if(!directory::exists(pathname)) return;
string type = extension(pathname); string type = extension(pathname);
@@ -30,20 +20,15 @@ void Utility::loadMedia(string pathname) {
for(auto& media : emulator->media) { for(auto& media : emulator->media) {
if(media.bootable == false) continue; if(media.bootable == false) continue;
if(type != media.type) continue; if(type != media.type) continue;
return loadMedia(emulator, media, {pathname, "/"}); loadMedia(emulator, media, {pathname, "/"});
libraryManager->setVisible(false);
return;
} }
} }
MessageWindow().setText("Unable to determine media type.").warning(); MessageWindow().setText("Unable to determine media type.").warning();
} }
//load menu option selected
void Utility::loadMedia(Emulator::Interface* emulator, Emulator::Interface::Media& media) {
string pathname = browser->select({"Load ", media.name}, media.type);
if(!directory::exists(pathname)) return;
return loadMedia(emulator, media, pathname);
}
//load base cartridge //load base cartridge
void Utility::loadMedia(Emulator::Interface* emulator, Emulator::Interface::Media& media, string pathname) { void Utility::loadMedia(Emulator::Interface* emulator, Emulator::Interface::Media& media, string pathname) {
unload(); unload();
@@ -62,7 +47,7 @@ void Utility::loadMedia(Emulator::Interface* emulator, Emulator::Interface::Medi
//request from emulation core to load non-volatile media folder //request from emulation core to load non-volatile media folder
void Utility::loadRequest(unsigned id, string name, string type) { void Utility::loadRequest(unsigned id, string name, string type) {
string pathname = browser->select({"Load ", name}, type); string pathname = libraryManager->load(type); //browser->select({"Load ", name}, type);
if(pathname.empty()) return; if(pathname.empty()) return;
path(id) = pathname; path(id) = pathname;
this->pathname.append(pathname); this->pathname.append(pathname);
@@ -318,6 +303,13 @@ void Utility::showMessage(string message) {
statusMessage = message; statusMessage = message;
} }
string Utility::libraryPath() {
string path = string::read({configpath(), "higan/library.bml"}).strip().ltrim<1>("Path: ").transform("\\", "/");
if(path.empty()) path = {userpath(), "Emulation/"};
if(path.endswith("/") == false) path.append("/");
return path;
}
Utility::Utility() { Utility::Utility() {
tracerEnable = false; tracerEnable = false;
statusTime = 0; statusTime = 0;

View File

@@ -2,7 +2,6 @@ struct Utility {
void setInterface(Emulator::Interface* emulator); void setInterface(Emulator::Interface* emulator);
void loadMedia(string pathname); void loadMedia(string pathname);
void loadMedia(Emulator::Interface* emulator, Emulator::Interface::Media& media);
void loadMedia(Emulator::Interface* emulator, Emulator::Interface::Media& media, string pathname); void loadMedia(Emulator::Interface* emulator, Emulator::Interface::Media& media, string pathname);
void loadRequest(unsigned id, string name, string type); void loadRequest(unsigned id, string name, string type);
@@ -30,6 +29,8 @@ struct Utility {
void setStatusText(string text); void setStatusText(string text);
void showMessage(string message); void showMessage(string message);
string libraryPath();
Utility(); Utility();
lstring path; lstring path;