mirror of https://github.com/bsnes-emu/bsnes.git
Update to v093r04 release.
byuu says: This version replaces the old folder-browser with a proper game library.
This commit is contained in:
parent
68eaf53691
commit
b4f18c3b47
|
@ -3,7 +3,7 @@
|
|||
|
||||
namespace Emulator {
|
||||
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 License[] = "GPLv3";
|
||||
static const char Website[] = "http://byuu.org/";
|
||||
|
|
|
@ -15,7 +15,6 @@ using namespace nall;
|
|||
|
||||
namespace phoenix {
|
||||
#include "state.hpp"
|
||||
#include "utility.cpp"
|
||||
#include "layout/fixed-layout.cpp"
|
||||
#include "layout/horizontal-layout.cpp"
|
||||
#include "layout/vertical-layout.cpp"
|
||||
|
|
|
@ -243,7 +243,6 @@ struct pWidget : public pSizable {
|
|||
GtkWidget* gtkParent = nullptr;
|
||||
|
||||
virtual GtkWidget* container(Widget& widget);
|
||||
virtual Position containerOffset();
|
||||
virtual bool focused();
|
||||
virtual Size minimumSize();
|
||||
virtual void setEnabled(bool enabled);
|
||||
|
@ -545,7 +544,6 @@ struct pTabFrame : public pWidget {
|
|||
|
||||
void append(string text, const image& image);
|
||||
GtkWidget* container(Widget& widget);
|
||||
Position containerOffset();
|
||||
Position displacement();
|
||||
void remove(unsigned selection);
|
||||
void setEnabled(bool enabled);
|
||||
|
|
|
@ -73,10 +73,6 @@ static Widget* GetParentWidget(Sizable* sizable) {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
static bool HasParentWidget(Sizable* sizable) {
|
||||
return GetParentWidget(sizable) != nullptr;
|
||||
}
|
||||
|
||||
static Keyboard::Keycode Keysym(unsigned keysym) {
|
||||
switch(keysym) {
|
||||
case GDK_Escape: return Keyboard::Keycode::Escape;
|
||||
|
|
|
@ -38,10 +38,6 @@ GtkWidget* pTabFrame::container(Widget& widget) {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
Position pTabFrame::containerOffset() {
|
||||
return {widget.state.geometry.x + 3, widget.state.geometry.y + 28};
|
||||
}
|
||||
|
||||
Position pTabFrame::displacement() {
|
||||
return {6, 31};
|
||||
}
|
||||
|
@ -63,8 +59,7 @@ void pTabFrame::setGeometry(Geometry geometry) {
|
|||
geometry.x += 1, geometry.width -= 5;
|
||||
geometry.y += 26, geometry.height -= 31;
|
||||
for(auto& layout : tabFrame.state.layout) {
|
||||
if(layout == nullptr) continue;
|
||||
layout->setGeometry(geometry);
|
||||
if(layout) layout->setGeometry(geometry);
|
||||
}
|
||||
synchronizeLayout();
|
||||
}
|
||||
|
|
|
@ -4,10 +4,6 @@ GtkWidget* pWidget::container(Widget& widget) {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
Position pWidget::containerOffset() {
|
||||
return {0, 0};
|
||||
}
|
||||
|
||||
bool pWidget::focused() {
|
||||
return GTK_WIDGET_HAS_FOCUS(gtkWidget);
|
||||
}
|
||||
|
|
|
@ -153,7 +153,7 @@ void pWindow::append(Widget& widget) {
|
|||
widget.setFont(window.state.widgetFont);
|
||||
}
|
||||
|
||||
if(HasParentWidget(&widget)) {
|
||||
if(GetParentWidget(&widget)) {
|
||||
widget.p.gtkParent = GetParentWidget(&widget)->p.container(widget);
|
||||
} else {
|
||||
widget.p.gtkParent = formContainer;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/****************************************************************************
|
||||
** 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)
|
||||
**
|
||||
** WARNING! All changes made in this file will be lost!
|
||||
|
|
|
@ -127,8 +127,6 @@ public:
|
|||
QStatusBar* qtStatus;
|
||||
QWidget* qtContainer;
|
||||
|
||||
static Window& none();
|
||||
|
||||
void append(Layout& layout);
|
||||
void append(Menu& menu);
|
||||
void append(Widget& widget);
|
||||
|
@ -255,6 +253,8 @@ public slots:
|
|||
struct pSizable : public pObject {
|
||||
Sizable& sizable;
|
||||
|
||||
virtual Position displacement() { return {0, 0}; }
|
||||
|
||||
pSizable(Sizable& sizable) : pObject(sizable), sizable(sizable) {}
|
||||
|
||||
void constructor() {}
|
||||
|
@ -274,6 +274,7 @@ struct pWidget : public pSizable {
|
|||
Widget& widget;
|
||||
QWidget* qtWidget;
|
||||
|
||||
virtual QWidget* container(Widget& widget);
|
||||
bool focused();
|
||||
virtual Size minimumSize();
|
||||
virtual void setEnabled(bool enabled);
|
||||
|
@ -664,6 +665,8 @@ public:
|
|||
QTabWidget* qtTabFrame;
|
||||
|
||||
void append(string text, const image& image);
|
||||
QWidget* container(Widget& widget);
|
||||
Position displacement();
|
||||
void remove(unsigned selection);
|
||||
void setEnabled(bool enabled);
|
||||
void setGeometry(Geometry geometry);
|
||||
|
|
|
@ -23,6 +23,33 @@ static lstring DropPaths(QDropEvent* event) {
|
|||
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) {
|
||||
switch(keysym) {
|
||||
case XK_Escape: return Keyboard::Keycode::Escape;
|
||||
|
|
|
@ -10,7 +10,7 @@ void pFrame::setGeometry(Geometry geometry) {
|
|||
if(frame.state.layout == nullptr) return;
|
||||
Size size = pFont::size(widget.state.font, frame.state.text);
|
||||
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;
|
||||
frame.state.layout->setGeometry(geometry);
|
||||
}
|
||||
|
@ -26,6 +26,14 @@ void pFrame::setVisible(bool visible) {
|
|||
|
||||
void pFrame::constructor() {
|
||||
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();
|
||||
setText(frame.state.text);
|
||||
|
|
|
@ -2,11 +2,24 @@ namespace phoenix {
|
|||
|
||||
void pTabFrame::append(string text, const image& image) {
|
||||
unsigned selection = tabFrame.state.text.size() - 1;
|
||||
QWidget* widget = new QWidget; //addTab() requires a child widget, so give it an empty one
|
||||
qtTabFrame->addTab(widget, QString::fromUtf8(text));
|
||||
qtTabFrame->addTab(new QWidget, QString::fromUtf8(text));
|
||||
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) {
|
||||
qtTabFrame->removeTab(selection);
|
||||
}
|
||||
|
@ -20,11 +33,10 @@ void pTabFrame::setEnabled(bool enabled) {
|
|||
|
||||
void pTabFrame::setGeometry(Geometry geometry) {
|
||||
pWidget::setGeometry(geometry);
|
||||
geometry.x += 1, geometry.width -= 2;
|
||||
geometry.y += 29, geometry.height -= 30;
|
||||
geometry.x += 0, geometry.width -= 5;
|
||||
geometry.y += 29, geometry.height -= 33;
|
||||
for(auto& layout : tabFrame.state.layout) {
|
||||
if(layout == nullptr) continue;
|
||||
layout->setGeometry(geometry);
|
||||
if(layout) layout->setGeometry(geometry);
|
||||
}
|
||||
synchronizeLayout();
|
||||
}
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
namespace phoenix {
|
||||
|
||||
QWidget* pWidget::container(Widget& widget) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool pWidget::focused() {
|
||||
return qtWidget->hasFocus();
|
||||
}
|
||||
|
@ -24,6 +28,10 @@ void pWidget::setFont(string font) {
|
|||
}
|
||||
|
||||
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);
|
||||
if(widget.onSize) widget.onSize();
|
||||
}
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
namespace phoenix {
|
||||
|
||||
Window& pWindow::none() {
|
||||
static Window* window = nullptr;
|
||||
if(window == nullptr) window = new Window;
|
||||
return *window;
|
||||
}
|
||||
|
||||
void pWindow::append(Layout& layout) {
|
||||
Geometry geometry = window.state.geometry;
|
||||
geometry.x = geometry.y = 0;
|
||||
|
@ -14,7 +8,7 @@ void pWindow::append(Layout& layout) {
|
|||
|
||||
void pWindow::append(Menu& menu) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -22,8 +16,12 @@ void pWindow::append(Widget& widget) {
|
|||
if(widget.font().empty() && !window.state.widgetFont.empty()) {
|
||||
widget.setFont(window.state.widgetFont);
|
||||
}
|
||||
if(widget.font().empty()) widget.p.setFont("Sans, 8");
|
||||
widget.p.qtWidget->setParent(qtContainer);
|
||||
if(widget.font().empty()) widget.p.setFont(Font::sans(8));
|
||||
if(GetParentWidget(&widget)) {
|
||||
widget.p.qtWidget->setParent(GetParentWidget(&widget)->p.container(widget));
|
||||
} else {
|
||||
widget.p.qtWidget->setParent(qtContainer);
|
||||
}
|
||||
widget.setVisible(widget.visible());
|
||||
}
|
||||
|
||||
|
@ -69,10 +67,11 @@ void pWindow::remove(Widget& widget) {
|
|||
|
||||
void pWindow::setBackgroundColor(Color color) {
|
||||
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->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) {
|
||||
|
|
|
@ -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_ENTERMENULOOP: case WM_ENTERSIZEMOVE: window.p.onModalBegin(); 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);
|
||||
|
|
|
@ -14,3 +14,5 @@
|
|||
#include <shlobj.h>
|
||||
#include <nall/windows/registry.hpp>
|
||||
#include <nall/windows/utf8.hpp>
|
||||
|
||||
#define TBS_TRANSPARENTBKGND 0x1000
|
||||
|
|
|
@ -375,8 +375,8 @@ struct pFrame : public pWidget {
|
|||
|
||||
struct pHexEdit : public pWidget {
|
||||
HexEdit& hexEdit;
|
||||
HWND scrollBar;
|
||||
LRESULT CALLBACK (*windowProc)(HWND, UINT, LPARAM, WPARAM);
|
||||
WindowProc windowProc = nullptr;
|
||||
HWND scrollBar = nullptr;
|
||||
|
||||
void setColumns(unsigned columns);
|
||||
void setLength(unsigned length);
|
||||
|
|
|
@ -74,6 +74,19 @@ static unsigned GetWindowZOrder(HWND hwnd) {
|
|||
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) {
|
||||
#define pressed(keysym) (GetAsyncKeyState(keysym) & 0x8000)
|
||||
#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);
|
||||
|
||||
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: {
|
||||
unsigned id = LOWORD(wparam);
|
||||
HWND control = GetDlgItem(hwnd, id);
|
||||
Object* object = (Object*)GetWindowLongPtr(control, GWLP_USERDATA);
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -245,16 +280,16 @@ static LRESULT CALLBACK Shared_windowProc(WindowProc windowProc, HWND hwnd, UINT
|
|||
HWND control = GetDlgItem(hwnd, id);
|
||||
Object* object = control ? (Object*)GetWindowLongPtr(control, GWLP_USERDATA) : (Object*)(&pObject::find(id)->object);
|
||||
if(object == nullptr) break;
|
||||
if(dynamic_cast<Item*>(object)) { ((Item*)object)->p.onActivate(); break; }
|
||||
if(dynamic_cast<CheckItem*>(object)) { ((CheckItem*)object)->p.onToggle(); break; }
|
||||
if(dynamic_cast<Button*>(object)) { ((Button*)object)->p.onActivate(); break; }
|
||||
if(dynamic_cast<CheckButton*>(object)) { ((CheckButton*)object)->p.onToggle(); break; }
|
||||
if(dynamic_cast<CheckLabel*>(object)) { ((CheckLabel*)object)->p.onToggle(); break; }
|
||||
if(dynamic_cast<ComboButton*>(object) && HIWORD(wparam) == CBN_SELCHANGE) { ((ComboButton*)object)->p.onChange(); break; }
|
||||
if(dynamic_cast<LineEdit*>(object) && HIWORD(wparam) == EN_CHANGE) { ((LineEdit*)object)->p.onChange(); break; }
|
||||
if(dynamic_cast<RadioButton*>(object)) { ((RadioButton*)object)->p.onActivate(); break; }
|
||||
if(dynamic_cast<RadioLabel*>(object)) { ((RadioLabel*)object)->p.onActivate(); break; }
|
||||
if(dynamic_cast<TextEdit*>(object) && HIWORD(wparam) == EN_CHANGE) { ((TextEdit*)object)->p.onChange(); break; }
|
||||
if(dynamic_cast<Item*>(object)) { ((Item*)object)->p.onActivate(); return FALSE; }
|
||||
if(dynamic_cast<CheckItem*>(object)) { ((CheckItem*)object)->p.onToggle(); return FALSE; }
|
||||
if(dynamic_cast<Button*>(object)) { ((Button*)object)->p.onActivate(); return FALSE; }
|
||||
if(dynamic_cast<CheckButton*>(object)) { ((CheckButton*)object)->p.onToggle(); return FALSE; }
|
||||
if(dynamic_cast<CheckLabel*>(object)) { ((CheckLabel*)object)->p.onToggle(); return FALSE; }
|
||||
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(); return FALSE; }
|
||||
if(dynamic_cast<RadioButton*>(object)) { ((RadioButton*)object)->p.onActivate(); return FALSE; }
|
||||
if(dynamic_cast<RadioLabel*>(object)) { ((RadioLabel*)object)->p.onActivate(); return FALSE; }
|
||||
if(dynamic_cast<TextEdit*>(object) && HIWORD(wparam) == EN_CHANGE) { ((TextEdit*)object)->p.onChange(); return FALSE; }
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -283,8 +318,8 @@ static LRESULT CALLBACK Shared_windowProc(WindowProc windowProc, HWND hwnd, UINT
|
|||
if(object == nullptr) break;
|
||||
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<HorizontalSlider*>(object)) { ((HorizontalSlider*)object)->p.onChange(); break; }
|
||||
if(dynamic_cast<VerticalSlider*>(object)) { ((VerticalSlider*)object)->p.onChange(); break; }
|
||||
if(dynamic_cast<HorizontalSlider*>(object)) { ((HorizontalSlider*)object)->p.onChange(); return TRUE; }
|
||||
if(dynamic_cast<VerticalSlider*>(object)) { ((VerticalSlider*)object)->p.onChange(); return TRUE; }
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ void pComboButton::reset() {
|
|||
}
|
||||
|
||||
void pComboButton::setGeometry(Geometry geometry) {
|
||||
SetWindowPos(hwnd, NULL, geometry.x, geometry.y, geometry.width, 1, SWP_NOZORDER);
|
||||
pWidget::setGeometry(geometry);
|
||||
RECT rc;
|
||||
GetWindowRect(hwnd, &rc);
|
||||
unsigned adjustedHeight = geometry.height - ((rc.bottom - rc.top) - SendMessage(hwnd, CB_GETITEMHEIGHT, (WPARAM)-1, 0));
|
||||
|
|
|
@ -4,7 +4,6 @@ static LRESULT CALLBACK HexEdit_windowProc(HWND hwnd, UINT msg, WPARAM wparam, L
|
|||
HexEdit& hexEdit = *(HexEdit*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
|
||||
switch(msg) {
|
||||
|
||||
case WM_KEYDOWN:
|
||||
if(hexEdit.p.keyPress(wparam)) return 0;
|
||||
break;
|
||||
|
@ -45,7 +44,6 @@ static LRESULT CALLBACK HexEdit_windowProc(HWND hwnd, UINT msg, WPARAM wparam, L
|
|||
hexEdit.p.scrollTo(info.nPos);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return hexEdit.p.windowProc(hwnd, msg, wparam, lparam);
|
||||
|
@ -123,7 +121,7 @@ void pHexEdit::constructor() {
|
|||
);
|
||||
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);
|
||||
|
||||
setDefaultFont();
|
||||
|
|
|
@ -17,10 +17,11 @@ void pHorizontalSlider::setPosition(unsigned position) {
|
|||
|
||||
void pHorizontalSlider::constructor() {
|
||||
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
|
||||
);
|
||||
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&horizontalSlider);
|
||||
|
||||
unsigned position = horizontalSlider.state.position;
|
||||
setLength(horizontalSlider.state.length);
|
||||
horizontalSlider.setPosition(position);
|
||||
|
|
|
@ -36,19 +36,13 @@ static LRESULT CALLBACK Label_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPA
|
|||
Window* window = (Window*)label->Sizable::state.window;
|
||||
if(window == nullptr) return DefWindowProc(hwnd, msg, wparam, lparam);
|
||||
|
||||
if(msg == WM_GETDLGCODE) {
|
||||
return DLGC_STATIC | DLGC_WANTCHARS;
|
||||
}
|
||||
|
||||
if(msg == WM_ERASEBKGND) {
|
||||
//background is erased during WM_PAINT to prevent flickering
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if(msg == WM_PAINT) {
|
||||
switch(msg) {
|
||||
case WM_GETDLGCODE: return DLGC_STATIC | DLGC_WANTCHARS;
|
||||
case WM_ERASEBKGND: return TRUE;
|
||||
case WM_PAINT: {
|
||||
PAINTSTRUCT ps;
|
||||
RECT rc;
|
||||
BeginPaint(hwnd, &ps);
|
||||
RECT rc;
|
||||
GetClientRect(hwnd, &rc);
|
||||
DrawThemeParentBackground(hwnd, ps.hdc, &rc);
|
||||
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;
|
||||
DrawText(ps.hdc, text, -1, &rc, DT_LEFT | DT_END_ELLIPSIS);
|
||||
EndPaint(hwnd, &ps);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return DefWindowProc(hwnd, msg, wparam, lparam);
|
||||
|
|
|
@ -23,19 +23,6 @@ void ListView_SetImage(HWND hwnd, HIMAGELIST imageList, unsigned row, unsigned c
|
|||
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) {
|
||||
wchar_t empty[] = L"";
|
||||
unsigned row = ListView_GetItemCount(hwnd);
|
||||
|
@ -122,7 +109,7 @@ void pListView::setImage(unsigned selection, unsigned position, const image& ima
|
|||
//append and assign new image
|
||||
imageMap(selection)(position) = images.size();
|
||||
images.append(image);
|
||||
ImageList_Append(imageList, image);
|
||||
ImageList_Append(imageList, image, 15);
|
||||
ListView_SetImage(hwnd, imageList, selection, position, imageMap(selection)(position));
|
||||
}
|
||||
|
||||
|
@ -211,7 +198,7 @@ void pListView::buildImageList() {
|
|||
}
|
||||
|
||||
//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;
|
||||
|
||||
//set images for all cells
|
||||
|
|
|
@ -94,7 +94,7 @@ void pTabFrame::buildImageList() {
|
|||
unsigned size = pFont::size(hfont, " ").height;
|
||||
imageList = ImageList_Create(size, size, ILC_COLOR32, 1, 0);
|
||||
for(auto& image : tabFrame.state.image) {
|
||||
ImageList_Append(imageList, image);
|
||||
ImageList_Append(imageList, image, size);
|
||||
}
|
||||
TabCtrl_SetImageList(hwnd, imageList);
|
||||
for(unsigned n = 0; n < tabFrame.state.image.size(); n++) {
|
||||
|
|
|
@ -17,10 +17,11 @@ void pVerticalSlider::setPosition(unsigned position) {
|
|||
|
||||
void pVerticalSlider::constructor() {
|
||||
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
|
||||
);
|
||||
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&verticalSlider);
|
||||
|
||||
unsigned position = verticalSlider.state.position;
|
||||
setLength(verticalSlider.state.length);
|
||||
verticalSlider.setPosition(position);
|
||||
|
|
|
@ -41,6 +41,10 @@ ConfigurationSettings::ConfigurationSettings() {
|
|||
server.append(server.password = "", "Password");
|
||||
append(server, "Server");
|
||||
|
||||
library.append(library.selection = 0, "Selection");
|
||||
library.append(library.showOnStartup = true, "ShowOnStartup");
|
||||
append(library, "Library");
|
||||
|
||||
load();
|
||||
}
|
||||
|
||||
|
|
|
@ -45,6 +45,11 @@ struct ConfigurationSettings : Configuration::Document {
|
|||
string password;
|
||||
} server;
|
||||
|
||||
struct Library : Configuration::Node {
|
||||
unsigned selection;
|
||||
bool showOnStartup;
|
||||
} library;
|
||||
|
||||
void load();
|
||||
void save();
|
||||
ConfigurationSettings();
|
||||
|
|
|
@ -66,7 +66,7 @@ Program::Program(int argc, char** argv) {
|
|||
utility = new Utility;
|
||||
inputManager = new InputManager;
|
||||
windowManager = new WindowManager;
|
||||
browser = new Browser;
|
||||
libraryManager = new LibraryManager;
|
||||
presentation = new Presentation;
|
||||
dipSwitches = new DipSwitches;
|
||||
videoSettings = new VideoSettings;
|
||||
|
@ -85,6 +85,8 @@ Program::Program(int argc, char** argv) {
|
|||
presentation->setVisible();
|
||||
utility->resize();
|
||||
|
||||
if(config->library.showOnStartup) libraryManager->setVisible();
|
||||
|
||||
video.set(Video::Handle, presentation->viewport.handle());
|
||||
if(!video.cap(Video::Depth) || !video.set(Video::Depth, depth = 30u)) {
|
||||
video.set(Video::Depth, depth = 24u);
|
||||
|
@ -114,7 +116,6 @@ Program::Program(int argc, char** argv) {
|
|||
|
||||
utility->unload();
|
||||
config->save();
|
||||
browser->saveConfiguration();
|
||||
inputManager->saveConfiguration();
|
||||
windowManager->saveGeometry();
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
|
@ -17,6 +17,7 @@ DipSwitches::DipSwitches() {
|
|||
controlLayout.append(accept, {80, 0});
|
||||
|
||||
setGeometry({128, 128, 250, layout.minimumSize().height});
|
||||
windowManager->append(this, "DipSwitches");
|
||||
|
||||
onClose = accept.onActivate = [&] {
|
||||
setModal(false);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#include "../ethos.hpp"
|
||||
#include "browser.cpp"
|
||||
#include "library.cpp"
|
||||
#include "presentation.cpp"
|
||||
#include "dip-switches.cpp"
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
#include "browser.hpp"
|
||||
#include "library.hpp"
|
||||
#include "presentation.hpp"
|
||||
#include "dip-switches.hpp"
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
|
@ -62,7 +62,7 @@ Presentation::Presentation() {
|
|||
viewport.setDroppable();
|
||||
|
||||
loadMenu.setText("Library");
|
||||
loadImport.setText("Import Game ...");
|
||||
loadGame.setText("Load Game ...");
|
||||
settingsMenu.setText("Settings");
|
||||
videoMenu.setText("Video");
|
||||
centerVideo.setText("Center");
|
||||
|
@ -90,11 +90,7 @@ Presentation::Presentation() {
|
|||
synchronizeTime.setText("Synchronize Time");
|
||||
|
||||
append(loadMenu);
|
||||
for(auto& item : loadListSystem) loadMenu.append(*item);
|
||||
if(program->ananke.open()) {
|
||||
loadMenu.append(loadSeparator);
|
||||
loadMenu.append(loadImport);
|
||||
}
|
||||
loadMenu.append(loadGame);
|
||||
for(auto& systemItem : emulatorList) append(systemItem->menu);
|
||||
append(settingsMenu);
|
||||
settingsMenu.append(videoMenu);
|
||||
|
@ -154,15 +150,7 @@ Presentation::Presentation() {
|
|||
}
|
||||
};
|
||||
|
||||
loadImport.onActivate = [&] {
|
||||
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);
|
||||
};
|
||||
|
||||
loadGame.onActivate = [&] { libraryManager->setVisible(); };
|
||||
shaderNone.onActivate = [&] { config->video.shader = "None"; utility->updateShader(); };
|
||||
shaderBlur.onActivate = [&] { config->video.shader = "Blur"; utility->updateShader(); };
|
||||
shaderEmulation.onActivate = [&] { config->video.shader = "Display Emulation"; utility->updateShader(); };
|
||||
|
@ -190,16 +178,6 @@ void Presentation::bootstrap() {
|
|||
auto iEmulator = new 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->power.setText("Power");
|
||||
iEmulator->reset.setText("Reset");
|
||||
|
|
|
@ -22,9 +22,7 @@ struct Presentation : Window {
|
|||
Emulator* active = nullptr;
|
||||
|
||||
Menu loadMenu;
|
||||
vector<Item*> loadListSystem;
|
||||
Separator loadSeparator;
|
||||
Item loadImport;
|
||||
Item loadGame;
|
||||
Menu settingsMenu;
|
||||
Menu videoMenu;
|
||||
RadioItem centerVideo;
|
||||
|
|
|
@ -2,19 +2,19 @@ AdvancedSettings* advancedSettings = nullptr;
|
|||
|
||||
AdvancedSettings::AdvancedSettings() {
|
||||
driverTitle.setFont(program->boldFont);
|
||||
driverTitle.setText("Driver Selection");
|
||||
driverTitle.setText("Driver Selection:");
|
||||
videoLabel.setText("Video:");
|
||||
audioLabel.setText("Audio:");
|
||||
inputLabel.setText("Input:");
|
||||
libraryTitle.setFont(program->boldFont);
|
||||
libraryTitle.setText("Game Library Path");
|
||||
libraryTitle.setText("Game Library:");
|
||||
libraryLabel.setText("Path:");
|
||||
libraryPath.setEditable(false);
|
||||
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("/");
|
||||
libraryPath.setText(path);
|
||||
libraryPath.setText(utility->libraryPath());
|
||||
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.setText({
|
||||
Emulator::Name, " v", Emulator::Version, "\n",
|
||||
|
@ -53,10 +53,12 @@ AdvancedSettings::AdvancedSettings() {
|
|||
driverLayout.append(inputLabel, {0, 0}, 5);
|
||||
driverLayout.append(inputDriver, {~0, 0});
|
||||
append(libraryTitle, {~0, 0});
|
||||
append(libraryLayout, {~0, 0}, 15);
|
||||
append(libraryLayout, {~0, 0});
|
||||
libraryLayout.append(libraryLabel, {0, 0}, 5);
|
||||
libraryLayout.append(libraryPath, {~0, 0}, 5);
|
||||
libraryLayout.append(libraryBrowse, {80, 0});
|
||||
append(libraryShowOnStartup, {~0, 0}, 15);
|
||||
append(information, {~0, 0}, 15);
|
||||
if(Intrinsics::platform() != Intrinsics::Platform::MacOSX) {
|
||||
append(spacer, {~0, ~0});
|
||||
append(infoLabel, {~0, 0});
|
||||
|
@ -72,4 +74,8 @@ AdvancedSettings::AdvancedSettings() {
|
|||
file::write({configpath(), "higan/library.bml"}, {"Path: ", path, "\n"});
|
||||
libraryPath.setText(path);
|
||||
};
|
||||
|
||||
libraryShowOnStartup.onToggle = [&] {
|
||||
config->library.showOnStartup = libraryShowOnStartup.checked();
|
||||
};
|
||||
}
|
||||
|
|
|
@ -13,6 +13,9 @@ struct AdvancedSettings : SettingsLayout {
|
|||
Label libraryLabel;
|
||||
LineEdit libraryPath;
|
||||
Button libraryBrowse;
|
||||
CheckLabel libraryShowOnStartup;
|
||||
|
||||
Label information;
|
||||
|
||||
Widget spacer;
|
||||
Label infoLabel;
|
||||
|
|
|
@ -12,16 +12,6 @@ void Utility::loadMedia(string pathname) {
|
|||
pathname.transform("\\", "/");
|
||||
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;
|
||||
string type = extension(pathname);
|
||||
|
||||
|
@ -30,20 +20,15 @@ void Utility::loadMedia(string pathname) {
|
|||
for(auto& media : emulator->media) {
|
||||
if(media.bootable == false) 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();
|
||||
}
|
||||
|
||||
//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
|
||||
void Utility::loadMedia(Emulator::Interface* emulator, Emulator::Interface::Media& media, string pathname) {
|
||||
unload();
|
||||
|
@ -62,7 +47,7 @@ void Utility::loadMedia(Emulator::Interface* emulator, Emulator::Interface::Medi
|
|||
|
||||
//request from emulation core to load non-volatile media folder
|
||||
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;
|
||||
path(id) = pathname;
|
||||
this->pathname.append(pathname);
|
||||
|
@ -318,6 +303,13 @@ void Utility::showMessage(string 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() {
|
||||
tracerEnable = false;
|
||||
statusTime = 0;
|
||||
|
|
|
@ -2,7 +2,6 @@ struct Utility {
|
|||
void setInterface(Emulator::Interface* emulator);
|
||||
|
||||
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 loadRequest(unsigned id, string name, string type);
|
||||
|
@ -30,6 +29,8 @@ struct Utility {
|
|||
void setStatusText(string text);
|
||||
void showMessage(string message);
|
||||
|
||||
string libraryPath();
|
||||
|
||||
Utility();
|
||||
|
||||
lstring path;
|
||||
|
|
Loading…
Reference in New Issue