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 {
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/";

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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_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);

View File

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

View File

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

View File

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

View File

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

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

View File

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

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;
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);

View File

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

View File

@ -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++) {

View File

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

View File

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

View File

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

View File

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

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});
setGeometry({128, 128, 250, layout.minimumSize().height});
windowManager->append(this, "DipSwitches");
onClose = accept.onActivate = [&] {
setModal(false);

View File

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

View File

@ -1,3 +1,3 @@
#include "browser.hpp"
#include "library.hpp"
#include "presentation.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();
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");

View File

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

View File

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

View File

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

View File

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

View File

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