Update to v094r24 release.

byuu says:

Finally!! Compilation works once again on Windows.

However, it's pretty buggy. Modality isn't really working right, you can
still poke at other windows, but when you select ListView items, they
redraw as empty boxes (need to process WM_DRAWITEM before checking
modality.)

The program crashes when you close it (probably a ruby driver's term()
function, that's what it usually is.)

The Layout::setEnabled(false) call isn't working right, so you get that
annoying chiming sound and cursor movement when mapping keyboard keys to
game inputs.

The column sizing seems off a bit on first display for the Hotkeys tab.

And probably lots more.
This commit is contained in:
Tim Allen
2015-06-12 23:14:38 +10:00
parent 314aee8c5c
commit f0c17ffc0d
188 changed files with 5474 additions and 3834 deletions

View File

@@ -1,16 +1,46 @@
namespace phoenix {
#if defined(Hiro_Action)
void pAction::setEnabled(bool enabled) {
if(parentWindow) parentWindow->p.updateMenu();
namespace hiro {
auto pAction::construct() -> void {
}
void pAction::setVisible(bool visible) {
if(parentWindow) parentWindow->p.updateMenu();
auto pAction::destruct() -> void {
}
void pAction::constructor() {
parentMenu = 0;
parentWindow = 0;
auto pAction::setEnabled(bool enabled) -> void {
_synchronize();
}
auto pAction::setVisible(bool visible) -> void {
_synchronize();
}
auto pAction::_parentMenu() -> maybe<pMenu&> {
if(auto parent = self().parentMenu()) {
if(auto self = parent->self()) return *self;
}
return nothing;
}
auto pAction::_parentMenuBar() -> maybe<pMenuBar&> {
if(auto parent = self().parentMenuBar(true)) {
if(auto self = parent->self()) return *self;
}
return nothing;
}
auto pAction::_parentPopupMenu() -> maybe<pPopupMenu&> {
if(auto parent = self().parentPopupMenu(true)) {
if(auto self = parent->self()) return *self;
}
return nothing;
}
auto pAction::_synchronize() -> void {
if(auto parent = _parentMenuBar()) parent->_update();
}
}
#endif

View File

@@ -0,0 +1,21 @@
#if defined(Hiro_Action)
namespace hiro {
struct pAction : pObject {
Declare(Action, Object)
auto setEnabled(bool enabled) -> void;
auto setVisible(bool visible) -> void;
auto _parentMenu() -> maybe<pMenu&>;
auto _parentMenuBar() -> maybe<pMenuBar&>;
auto _parentPopupMenu() -> maybe<pPopupMenu&>;
auto _synchronize() -> void;
unsigned position = 0;
};
}
#endif

View File

@@ -1,24 +0,0 @@
namespace phoenix {
void pCheckItem::setChecked(bool checked) {
if(parentMenu) CheckMenuItem(parentMenu->p.hmenu, id, checked ? MF_CHECKED : MF_UNCHECKED);
}
void pCheckItem::setText(string text) {
if(parentWindow) parentWindow->p.updateMenu();
}
void pCheckItem::constructor() {
}
void pCheckItem::destructor() {
if(parentMenu) parentMenu->remove(checkItem);
}
void pCheckItem::onToggle() {
checkItem.state.checked = !checkItem.state.checked;
setChecked(checkItem.state.checked);
if(checkItem.onToggle) checkItem.onToggle();
}
}

View File

@@ -1,37 +0,0 @@
namespace phoenix {
void pItem::setImage(const image& image) {
createBitmap();
if(parentWindow) parentWindow->p.updateMenu();
}
void pItem::setText(string text) {
if(parentWindow) parentWindow->p.updateMenu();
}
void pItem::constructor() {
createBitmap();
}
void pItem::destructor() {
if(hbitmap) { DeleteObject(hbitmap); hbitmap = 0; }
if(parentMenu) parentMenu->remove(item);
}
void pItem::createBitmap() {
if(hbitmap) { DeleteObject(hbitmap); hbitmap = 0; }
if(item.state.image.width && item.state.image.height) {
nall::image nallImage = item.state.image;
nallImage.transform(0, 32, 255u << 24, 255u << 16, 255u << 8, 255u << 0);
nallImage.alphaBlend(GetSysColor(COLOR_MENU)); //Windows does not alpha blend menu icons properly (leaves black outline)
nallImage.scale(GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK), Interpolation::Linear);
hbitmap = CreateBitmap(nallImage);
}
}
void pItem::onActivate() {
if(item.onActivate) item.onActivate();
}
}

View File

@@ -0,0 +1,29 @@
#if defined(Hiro_MenuCheckItem)
namespace hiro {
auto pMenuCheckItem::construct() -> void {
}
auto pMenuCheckItem::destruct() -> void {
}
auto pMenuCheckItem::setChecked(bool checked) -> void {
if(auto menu = _parentMenu()) {
CheckMenuItem(menu->hmenu, position, MF_BYPOSITION | (checked ? MF_CHECKED : MF_UNCHECKED));
}
}
auto pMenuCheckItem::setText(const string& text) -> void {
_synchronize();
}
auto pMenuCheckItem::onToggle() -> void {
state().checked = !state().checked;
setChecked(state().checked);
self().doToggle();
}
}
#endif

View File

@@ -0,0 +1,16 @@
#if defined(Hiro_MenuCheckItem)
namespace hiro {
struct pMenuCheckItem : pAction {
Declare(MenuCheckItem, Action)
auto setChecked(bool checked) -> void;
auto setText(const string& text) -> void;
auto onToggle() -> void;
};
}
#endif

View File

@@ -0,0 +1,39 @@
#if defined(Hiro_MenuItem)
namespace hiro {
auto pMenuItem::construct() -> void {
_createBitmap();
}
auto pMenuItem::destruct() -> void {
if(hbitmap) { DeleteObject(hbitmap); hbitmap = nullptr; }
}
auto pMenuItem::setIcon(const image& icon) -> void {
_createBitmap();
_synchronize();
}
auto pMenuItem::setText(const string& text) -> void {
_synchronize();
}
auto pMenuItem::onActivate() -> void {
self().doActivate();
}
auto pMenuItem::_createBitmap() -> void {
if(hbitmap) { DeleteObject(hbitmap); hbitmap = nullptr; }
if(auto icon = state().icon) {
icon.transform(0, 32, 255u << 24, 255u << 16, 255u << 8, 255u << 0);
icon.alphaBlend(GetSysColor(COLOR_MENU)); //Windows does not alpha blend menu icons properly (leaves black outline)
icon.scale(GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK), Interpolation::Linear);
hbitmap = CreateBitmap(icon);
}
}
}
#endif

View File

@@ -0,0 +1,20 @@
#if defined(Hiro_MenuItem)
namespace hiro {
struct pMenuItem : pAction {
Declare(MenuItem, Action)
auto setIcon(const image& icon) -> void;
auto setText(const string& text) -> void;
auto onActivate() -> void;
auto _createBitmap() -> void;
HBITMAP hbitmap = 0;
};
}
#endif

View File

@@ -0,0 +1,49 @@
#if defined(Hiro_MenuRadioItem)
namespace hiro {
auto pMenuRadioItem::construct() -> void {
}
auto pMenuRadioItem::destruct() -> void {
}
auto pMenuRadioItem::setChecked() -> void {
if(auto group = self().group()) {
for(auto& weak : group->state.objects) {
if(auto object = weak.acquire()) {
if(auto menuRadioItem = dynamic_cast<mMenuRadioItem*>(object.data())) {
if(auto self = menuRadioItem->self()) {
if(auto menu = self->_parentMenu()) {
//CheckMenuRadioItem takes: lo, hi, id; checking only id when lo <= id <= hi
//hiro does not force IDs to be linear, so to uncheck id, we use: lo == hi == id + 1 (out of range)
//to check id, we use: lo == hi == id (only ID, but in range)
CheckMenuRadioItem(
menu->hmenu,
self->position, self->position, self->position + (position != self->position),
MF_BYPOSITION
);
}
}
}
}
}
}
}
auto pMenuRadioItem::setGroup(sGroup group) -> void {
}
auto pMenuRadioItem::setText(const string& text) -> void {
_synchronize();
}
auto pMenuRadioItem::onActivate() -> void {
if(state().checked) return;
self().setChecked();
self().doActivate();
}
}
#endif

View File

@@ -0,0 +1,17 @@
#if defined(Hiro_MenuRadioItem)
namespace hiro {
struct pMenuRadioItem : pAction {
Declare(MenuRadioItem, Action)
auto setChecked() -> void;
auto setGroup(sGroup group) -> void override;
auto setText(const string& text) -> void;
auto onActivate() -> void;
};
}
#endif

View File

@@ -0,0 +1,13 @@
#if defined(Hiro_MenuSeparator)
namespace hiro {
auto pMenuSeparator::construct() -> void {
}
auto pMenuSeparator::destruct() -> void {
}
}
#endif

View File

@@ -0,0 +1,11 @@
#if defined(Hiro_MenuSeparator)
namespace hiro {
struct pMenuSeparator : pAction {
Declare(MenuSeparator, Action)
};
}
#endif

View File

@@ -1,113 +1,124 @@
namespace phoenix {
#if defined(Hiro_Menu)
void pMenu::append(Action& action) {
action.p.parentMenu = &menu;
if(parentWindow) parentWindow->p.updateMenu();
namespace hiro {
auto pMenu::construct() -> void {
_createBitmap();
}
void pMenu::remove(Action& action) {
if(parentWindow) parentWindow->p.updateMenu();
action.p.parentMenu = 0;
auto pMenu::destruct() -> void {
if(hbitmap) { DeleteObject(hbitmap); hbitmap = nullptr; }
if(hmenu) { DestroyMenu(hmenu); hmenu = nullptr; }
}
void pMenu::setImage(const image& image) {
createBitmap();
if(parentWindow) parentWindow->p.updateMenu();
auto pMenu::append(sAction action) -> void {
_synchronize();
}
void pMenu::setText(string text) {
if(parentWindow) parentWindow->p.updateMenu();
auto pMenu::remove(sAction action) -> void {
_synchronize();
}
void pMenu::constructor() {
hmenu = 0;
createBitmap();
auto pMenu::setIcon(const image& icon) -> void {
_createBitmap();
_synchronize();
}
void pMenu::destructor() {
if(hbitmap) { DeleteObject(hbitmap); hbitmap = 0; }
if(parentMenu) {
parentMenu->remove(menu);
} else if(parentWindow) {
//belongs to window's main menubar
parentWindow->remove(menu);
}
auto pMenu::setText(const string& text) -> void {
_synchronize();
}
void pMenu::createBitmap() {
auto pMenu::_createBitmap() -> void {
if(hbitmap) { DeleteObject(hbitmap); hbitmap = 0; }
if(menu.state.image.width && menu.state.image.height) {
nall::image nallImage = menu.state.image;
nallImage.transform(0, 32, 255u << 24, 255u << 16, 255u << 8, 255u << 0);
nallImage.alphaBlend(GetSysColor(COLOR_MENU)); //Windows does not alpha blend menu icons properly (leaves black outline)
nallImage.scale(GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK), Interpolation::Linear);
hbitmap = CreateBitmap(nallImage);
if(auto icon = state().icon) {
icon.transform(0, 32, 255u << 24, 255u << 16, 255u << 8, 255u << 0);
icon.alphaBlend(GetSysColor(COLOR_MENU)); //Windows does not alpha blend menu icons properly (leaves black outline)
icon.scale(GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK), Interpolation::Linear);
hbitmap = CreateBitmap(icon);
}
}
//Windows actions lack the ability to toggle visibility.
//To support this, menus must be destroyed and recreated when toggling any action's visibility.
void pMenu::update(Window& parentWindow, Menu* parentMenu) {
this->parentMenu = parentMenu;
this->parentWindow = &parentWindow;
auto pMenu::_update() -> void {
if(hmenu) DestroyMenu(hmenu);
hmenu = CreatePopupMenu();
for(auto& action : menu.state.action) {
action.p.parentMenu = &menu;
action.p.parentWindow = &parentWindow;
MENUINFO mi{sizeof(MENUINFO)};
mi.fMask = MIM_STYLE;
mi.dwStyle = MNS_NOTIFYBYPOS; //| MNS_MODELESS;
SetMenuInfo(hmenu, &mi);
unsigned enabled = action.state.enabled ? 0 : MF_GRAYED;
if(dynamic_cast<Menu*>(&action)) {
Menu& item = (Menu&)action;
if(action.state.visible) {
item.p.update(parentWindow, &menu);
AppendMenu(hmenu, MF_STRING | MF_POPUP | enabled, (UINT_PTR)item.p.hmenu, utf16_t(item.state.text));
unsigned position = 0;
if(item.state.image.width && item.state.image.height) {
MENUITEMINFO mii = {sizeof(MENUITEMINFO)};
for(auto& action : state().actions) {
if(!action->self()) continue;
action->self()->position = position;
unsigned enabled = action->enabled() ? 0 : MF_GRAYED;
MENUITEMINFO mii{sizeof(MENUITEMINFO)};
mii.fMask = MIIM_DATA;
mii.dwItemData = (ULONG_PTR)action.data();
if(auto menu = dynamic_cast<mMenu*>(action.data())) {
if(menu->visible()) {
menu->self()->_update();
AppendMenu(hmenu, MF_STRING | MF_POPUP | enabled, (UINT_PTR)menu->self()->hmenu, utf16_t(menu->text()));
if(auto bitmap = menu->self()->hbitmap) {
//Windows XP and below displays MIIM_BITMAP + hbmpItem in its own column (separate from check/radio marks)
//this causes too much spacing, so use a custom checkmark image instead
mii.fMask = MIIM_CHECKMARKS;
mii.hbmpUnchecked = item.p.hbitmap;
SetMenuItemInfo(hmenu, (UINT_PTR)item.p.hmenu, FALSE, &mii);
mii.fMask |= MIIM_CHECKMARKS;
mii.hbmpUnchecked = bitmap;
}
SetMenuItemInfo(hmenu, position++, true, &mii);
}
} else if(dynamic_cast<Separator*>(&action)) {
Separator& item = (Separator&)action;
if(action.state.visible) {
AppendMenu(hmenu, MF_SEPARATOR | enabled, item.p.id, L"");
}
} else if(dynamic_cast<Item*>(&action)) {
Item& item = (Item&)action;
if(action.state.visible) {
AppendMenu(hmenu, MF_STRING | enabled, item.p.id, utf16_t(item.state.text));
if(item.state.image.width && item.state.image.height) {
MENUITEMINFO mii = { sizeof(MENUITEMINFO) };
//Windows XP and below displays MIIM_BITMAP + hbmpItem in its own column (separate from check/radio marks)
//this causes too much spacing, so use a custom checkmark image instead
mii.fMask = MIIM_CHECKMARKS;
mii.hbmpUnchecked = item.p.hbitmap;
SetMenuItemInfo(hmenu, item.p.id, FALSE, &mii);
}
}
} else if(dynamic_cast<CheckItem*>(&action)) {
CheckItem& item = (CheckItem&)action;
if(action.state.visible) {
AppendMenu(hmenu, MF_STRING | enabled, item.p.id, utf16_t(item.state.text));
}
if(item.state.checked) item.setChecked();
} else if(dynamic_cast<RadioItem*>(&action)) {
RadioItem& item = (RadioItem&)action;
if(action.state.visible) {
AppendMenu(hmenu, MF_STRING | enabled, item.p.id, utf16_t(item.state.text));
}
if(item.state.checked) item.setChecked();
}
#if defined(Hiro_MenuSeparator)
else if(auto menuSeparator = dynamic_cast<mMenuSeparator*>(action.data())) {
if(menuSeparator->visible()) {
AppendMenu(hmenu, MF_SEPARATOR | enabled, position, L"");
SetMenuItemInfo(hmenu, position++, true, &mii);
}
}
#endif
#if defined(Hiro_MenuItem)
else if(auto menuItem = dynamic_cast<mMenuItem*>(action.data())) {
if(menuItem->visible()) {
AppendMenu(hmenu, MF_STRING | enabled, position, utf16_t(menuItem->text()));
if(auto bitmap = menuItem->self()->hbitmap) {
mii.fMask |= MIIM_CHECKMARKS;
mii.hbmpUnchecked = bitmap;
}
SetMenuItemInfo(hmenu, position++, true, &mii);
}
}
#endif
#if defined(Hiro_MenuCheckItem)
else if(auto menuCheckItem = dynamic_cast<mMenuCheckItem*>(action.data())) {
if(menuCheckItem->visible()) {
AppendMenu(hmenu, MF_STRING | enabled, position, utf16_t(menuCheckItem->text()));
SetMenuItemInfo(hmenu, position++, true, &mii);
if(menuCheckItem->checked()) menuCheckItem->setChecked();
}
}
#endif
#if defined(Hiro_MenuRadioItem)
else if(auto menuRadioItem = dynamic_cast<mMenuRadioItem*>(action.data())) {
if(menuRadioItem->visible()) {
AppendMenu(hmenu, MF_STRING | enabled, position, utf16_t(menuRadioItem->text()));
SetMenuItemInfo(hmenu, position++, true, &mii);
if(menuRadioItem->checked()) menuRadioItem->setChecked();
}
}
#endif
}
}
}
#endif

View File

@@ -0,0 +1,22 @@
#if defined(Hiro_Menu)
namespace hiro {
struct pMenu : pAction {
Declare(Menu, Action)
auto append(sAction action) -> void;
auto remove(sAction action) -> void;
auto setIcon(const image& icon) -> void;
auto setText(const string& text) -> void;
auto _createBitmap() -> void;
auto _update() -> void;
HMENU hmenu = 0;
HBITMAP hbitmap = 0;
};
}
#endif

View File

@@ -1,32 +0,0 @@
namespace phoenix {
void pRadioItem::setChecked() {
for(auto &item : radioItem.state.group) {
//CheckMenuRadioItem takes: lo, hi, id; checking only id when lo <= id <= hi
//phoenix does not force IDs to be linear, so to uncheck id, we use: lo == hi == id + 1 (out of range)
//to check id, we use: lo == hi == id (only ID, but in range)
if(item.p.parentMenu) CheckMenuRadioItem(item.p.parentMenu->p.hmenu, item.p.id, item.p.id, item.p.id + (id != item.p.id), MF_BYCOMMAND);
}
}
void pRadioItem::setGroup(const group<RadioItem>& group) {
}
void pRadioItem::setText(string text) {
if(parentWindow) parentWindow->p.updateMenu();
}
void pRadioItem::constructor() {
}
void pRadioItem::destructor() {
if(parentMenu) parentMenu->remove(radioItem);
}
void pRadioItem::onActivate() {
if(radioItem.state.checked) return;
radioItem.setChecked();
if(radioItem.onActivate) radioItem.onActivate();
}
}

View File

@@ -1,10 +0,0 @@
namespace phoenix {
void pSeparator::constructor() {
}
void pSeparator::destructor() {
if(parentMenu) parentMenu->remove(separator);
}
}

View File

@@ -1,14 +1,16 @@
namespace phoenix {
#if defined(Hiro_Application)
static bool Application_keyboardProc(HWND, UINT, WPARAM, LPARAM);
static void Application_processDialogMessage(MSG&);
static LRESULT CALLBACK Application_windowProc(HWND, UINT, WPARAM, LPARAM);
namespace hiro {
void pApplication::run() {
static auto Application_keyboardProc(HWND, UINT, WPARAM, LPARAM) -> bool;
static auto Application_processDialogMessage(MSG&) -> void;
static auto CALLBACK Application_windowProc(HWND, UINT, WPARAM, LPARAM) -> LRESULT;
auto pApplication::run() -> void {
MSG msg;
if(Application::main) {
while(applicationState.quit == false) {
Application::main();
if(Application::state.onMain) {
while(!Application::state.quit) {
Application::doMain();
processEvents();
}
} else {
@@ -19,12 +21,12 @@ void pApplication::run() {
}
}
bool pApplication::pendingEvents() {
auto pApplication::pendingEvents() -> bool {
MSG msg;
return PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE);
}
void pApplication::processEvents() {
auto pApplication::processEvents() -> void {
while(pendingEvents()) {
MSG msg;
if(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
@@ -33,7 +35,7 @@ void pApplication::processEvents() {
}
}
void Application_processDialogMessage(MSG& msg) {
auto Application_processDialogMessage(MSG& msg) -> void {
if(msg.message == WM_KEYDOWN || msg.message == WM_KEYUP
|| msg.message == WM_SYSKEYDOWN || msg.message == WM_SYSKEYUP) {
if(Application_keyboardProc(msg.hwnd, msg.message, msg.wParam, msg.lParam)) {
@@ -48,15 +50,17 @@ void Application_processDialogMessage(MSG& msg) {
}
}
void pApplication::quit() {
auto pApplication::quit() -> void {
PostQuitMessage(0);
}
void pApplication::initialize() {
auto pApplication::initialize() -> void {
CoInitialize(0);
InitCommonControls();
WNDCLASS wc;
#if defined(Hiro_Window)
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
@@ -64,11 +68,27 @@ void pApplication::initialize() {
wc.hIcon = LoadIcon(GetModuleHandle(0), MAKEINTRESOURCE(2));
wc.hInstance = GetModuleHandle(0);
wc.lpfnWndProc = Application_windowProc;
wc.lpszClassName = L"phoenix_window";
wc.lpszClassName = L"hiroWindow";
wc.lpszMenuName = 0;
wc.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass(&wc);
#endif
#if defined(Hiro_PopupMenu)
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hIcon = LoadIcon(GetModuleHandle(0), MAKEINTRESOURCE(2));
wc.hInstance = GetModuleHandle(0);
wc.lpfnWndProc = Menu_windowProc;
wc.lpszClassName = L"hiroPopupMenu";
wc.lpszMenuName = 0;
wc.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass(&wc);
#endif
#if defined(Hiro_Canvas)
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
@@ -76,11 +96,13 @@ void pApplication::initialize() {
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
wc.hInstance = GetModuleHandle(0);
wc.lpfnWndProc = Canvas_windowProc;
wc.lpszClassName = L"phoenix_canvas";
wc.lpszClassName = L"hiroCanvas";
wc.lpszMenuName = 0;
wc.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass(&wc);
#endif
#if defined(Hiro_Label)
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
@@ -88,11 +110,13 @@ void pApplication::initialize() {
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
wc.hInstance = GetModuleHandle(0);
wc.lpfnWndProc = Label_windowProc;
wc.lpszClassName = L"phoenix_label";
wc.lpszClassName = L"hiroLabel";
wc.lpszMenuName = 0;
wc.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass(&wc);
#endif
#if defined(Hiro_Viewport)
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = CreateSolidBrush(RGB(0, 0, 0));
@@ -100,76 +124,80 @@ void pApplication::initialize() {
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
wc.hInstance = GetModuleHandle(0);
wc.lpfnWndProc = Viewport_windowProc;
wc.lpszClassName = L"phoenix_viewport";
wc.lpszClassName = L"hiroViewport";
wc.lpszMenuName = 0;
wc.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass(&wc);
#endif
settings = new Settings;
pKeyboard::initialize();
}
static bool Application_keyboardProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
static auto Application_keyboardProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> bool {
if(msg != WM_KEYDOWN && msg != WM_SYSKEYDOWN && msg != WM_KEYUP && msg != WM_SYSKEYUP) return false;
GUITHREADINFO info;
memset(&info, 0, sizeof(GUITHREADINFO));
info.cbSize = sizeof(GUITHREADINFO);
GetGUIThreadInfo(GetCurrentThreadId(), &info);
Object* object = (Object*)GetWindowLongPtr(info.hwndFocus, GWLP_USERDATA);
if(object == nullptr) return false;
auto object = (mObject*)GetWindowLongPtr(info.hwndFocus, GWLP_USERDATA);
if(!object) return false;
if(dynamic_cast<Window*>(object)) {
Window& window = (Window&)*object;
if(pWindow::modal.size() > 0 && !pWindow::modal.find(&window.p)) return false;
Keyboard::Keycode keysym = Keysym(wparam, lparam);
if(keysym != Keyboard::Keycode::None) {
if((msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN) && window.onKeyPress) window.onKeyPress(keysym);
if((msg == WM_KEYUP || msg == WM_SYSKEYUP) && window.onKeyRelease) window.onKeyRelease(keysym);
if(auto window = dynamic_cast<mWindow*>(object)) {
if(pWindow::modal && !pWindow::modal.find(window->self())) return false;
if(auto code = pKeyboard::_translate(wparam, lparam)) {
if(msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN) window->doKeyPress(code);
if(msg == WM_KEYUP || msg == WM_SYSKEYUP) window->doKeyRelease(code);
}
return false;
}
if(msg == WM_KEYDOWN) {
if(dynamic_cast<ListView*>(object)) {
ListView& listView = (ListView&)*object;
if(0);
#if defined(Hiro_ListView)
else if(auto listView = dynamic_cast<mListView*>(object)) {
if(wparam == VK_RETURN) {
if(listView.selected()) return true; //returning true generates LVN_ITEMACTIVATE message
if(listView->selected()) return true; //returning true generates LVN_ITEMACTIVATE message
}
} else if(dynamic_cast<LineEdit*>(object)) {
LineEdit& lineEdit = (LineEdit&)*object;
}
#endif
#if defined(Hiro_LineEdit)
else if(auto lineEdit = dynamic_cast<mLineEdit*>(object)) {
if(wparam == VK_RETURN) {
if(lineEdit.onActivate) lineEdit.onActivate();
lineEdit->doActivate();
}
} else if(dynamic_cast<TextEdit*>(object)) {
TextEdit& textEdit = (TextEdit&)*object;
}
#endif
#if defined(Hiro_TextEdit)
else if(auto textEdit = dynamic_cast<mTextEdit*>(object)) {
if(wparam == 'A' && GetKeyState(VK_CONTROL) < 0) {
//Ctrl+A = select all text
//note: this is not a standard accelerator on Windows
Edit_SetSel(textEdit.p.hwnd, 0, ~0);
Edit_SetSel(textEdit->self()->hwnd, 0, ~0);
return true;
} else if(wparam == 'V' && GetKeyState(VK_CONTROL) < 0) {
//Ctrl+V = paste text
//note: this formats Unix (LF) and OS9 (CR) line-endings to Windows (CR+LF) line-endings
//this is necessary as the EDIT control only supports Windows line-endings
OpenClipboard(hwnd);
HANDLE handle = GetClipboardData(CF_UNICODETEXT);
if(handle) {
wchar_t* text = (wchar_t*)GlobalLock(handle);
if(text) {
if(auto handle = GetClipboardData(CF_UNICODETEXT)) {
if(auto text = (wchar_t*)GlobalLock(handle)) {
string data = (const char*)utf8_t(text);
data.replace("\r\n", "\n");
data.replace("\r", "\n");
data.replace("\n", "\r\n");
GlobalUnlock(handle);
utf16_t output(data);
HGLOBAL resource = GlobalAlloc(GMEM_MOVEABLE, (wcslen(output) + 1) * sizeof(wchar_t));
if(resource) {
wchar_t* write = (wchar_t*)GlobalLock(resource);
if(write) {
if(auto resource = GlobalAlloc(GMEM_MOVEABLE, (wcslen(output) + 1) * sizeof(wchar_t))) {
if(auto write = (wchar_t*)GlobalLock(resource)) {
wcscpy(write, output);
GlobalUnlock(write);
if(SetClipboardData(CF_UNICODETEXT, resource) == FALSE) {
if(SetClipboardData(CF_UNICODETEXT, resource) == NULL) {
GlobalFree(resource);
}
}
@@ -180,40 +208,45 @@ static bool Application_keyboardProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM
return false;
}
}
#endif
}
return false;
}
/*case WM_GETMINMAXINFO: {
MINMAXINFO* mmi = (MINMAXINFO*)lparam;
mmi->ptMinTrackSize.x = 256 + window.p.frameMargin().width;
mmi->ptMinTrackSize.y = 256 + window.p.frameMargin().height;
return TRUE;
break;
}*/
/*
case WM_GETMINMAXINFO: {
MINMAXINFO* mmi = (MINMAXINFO*)lparam;
mmi->ptMinTrackSize.x = 256 + window.p.frameMargin().width;
mmi->ptMinTrackSize.y = 256 + window.p.frameMargin().height;
return TRUE;
break;
}
*/
static LRESULT CALLBACK Application_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
Object* object = (Object*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if(object == nullptr) return DefWindowProc(hwnd, msg, wparam, lparam);
Window& window = dynamic_cast<Window*>(object) ? (Window&)*object : *((Widget*)object)->Sizable::state.window;
static auto CALLBACK Application_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> LRESULT {
auto object = (mObject*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if(!object) return DefWindowProc(hwnd, msg, wparam, lparam);
auto& window = dynamic_cast<mWindow*>(object) ? (mWindow&)*object : *object->parentWindow(true);
bool process = true;
if(!pWindow::modal.empty() && !pWindow::modal.find(&window.p)) process = false;
if(applicationState.quit) process = false;
if(process == false) return DefWindowProc(hwnd, msg, wparam, lparam);
if(pWindow::modal && !pWindow::modal.find(window.self())) process = false;
if(Application::state.quit) process = false;
if(!process) return DefWindowProc(hwnd, msg, wparam, lparam);
switch(msg) {
case WM_CLOSE: window.p.onClose(); return TRUE;
case WM_MOVE: window.p.onMove(); break;
case WM_SIZE: window.p.onSize(); break;
case WM_DROPFILES: window.p.onDrop(wparam); return FALSE;
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_CLOSE: window.self()->onClose(); return TRUE;
case WM_MOVE: window.self()->onMove(); break;
case WM_SIZE: window.self()->onSize(); break;
case WM_DROPFILES: window.self()->onDrop(wparam); return FALSE;
case WM_ERASEBKGND: if(window.self()->onEraseBackground()) return true; break;
case WM_ENTERMENULOOP: case WM_ENTERSIZEMOVE: window.self()->onModalBegin(); return FALSE;
case WM_EXITMENULOOP: case WM_EXITSIZEMOVE: window.self()->onModalEnd(); return FALSE;
}
return Shared_windowProc(DefWindowProc, hwnd, msg, wparam, lparam);
}
}
#endif

View File

@@ -0,0 +1,16 @@
#if defined(Hiro_Application)
namespace hiro {
struct pApplication {
static auto run() -> void;
static auto pendingEvents() -> bool;
static auto processEvents() -> void;
static auto quit() -> void;
static auto initialize() -> void;
};
}
#endif

View File

@@ -1,6 +1,8 @@
namespace phoenix {
#if defined(Hiro_BrowserWindow)
static int CALLBACK BrowserWindowCallbackProc(HWND hwnd, UINT msg, LPARAM lparam, LPARAM lpdata) {
namespace hiro {
static auto CALLBACK BrowserWindowCallbackProc(HWND hwnd, UINT msg, LPARAM lparam, LPARAM lpdata) -> signed {
if(msg == BFFM_INITIALIZED) {
if(lpdata) {
auto state = (BrowserWindow::State*)lpdata;
@@ -12,7 +14,7 @@ static int CALLBACK BrowserWindowCallbackProc(HWND hwnd, UINT msg, LPARAM lparam
return 0;
}
static string BrowserWindow_fileDialog(bool save, BrowserWindow::State& state) {
static auto BrowserWindow_fileDialog(bool save, BrowserWindow::State& state) -> string {
string path = string{state.path}.replace("/", "\\");
string filters;
@@ -46,7 +48,7 @@ static string BrowserWindow_fileDialog(bool save, BrowserWindow::State& state) {
OPENFILENAME ofn;
memset(&ofn, 0, sizeof(OPENFILENAME));
ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = state.parent ? state.parent->p.hwnd : 0;
ofn.hwndOwner = state.parent ? state.parent->self()->hwnd : 0;
ofn.lpstrFilter = wfilters;
ofn.lpstrInitialDir = wpath;
ofn.lpstrFile = wname;
@@ -62,11 +64,11 @@ static string BrowserWindow_fileDialog(bool save, BrowserWindow::State& state) {
return name;
}
string pBrowserWindow::directory(BrowserWindow::State& state) {
auto pBrowserWindow::directory(BrowserWindow::State& state) -> string {
wchar_t wname[PATH_MAX + 1] = L"";
BROWSEINFO bi;
bi.hwndOwner = state.parent ? state.parent->p.hwnd : 0;
bi.hwndOwner = state.parent ? state.parent->self()->hwnd : 0;
bi.pidlRoot = NULL;
bi.pszDisplayName = wname;
bi.lpszTitle = L"\nChoose a directory:";
@@ -94,12 +96,14 @@ string pBrowserWindow::directory(BrowserWindow::State& state) {
return name;
}
string pBrowserWindow::open(BrowserWindow::State& state) {
auto pBrowserWindow::open(BrowserWindow::State& state) -> string {
return BrowserWindow_fileDialog(0, state);
}
string pBrowserWindow::save(BrowserWindow::State& state) {
auto pBrowserWindow::save(BrowserWindow::State& state) -> string {
return BrowserWindow_fileDialog(1, state);
}
}
#endif

View File

@@ -0,0 +1,13 @@
#if defined(Hiro_BrowserWindow)
namespace hiro {
struct pBrowserWindow {
static auto directory(BrowserWindow::State& state) -> string;
static auto open(BrowserWindow::State& state) -> string;
static auto save(BrowserWindow::State& state) -> string;
};
}
#endif

View File

@@ -1,13 +1,17 @@
namespace phoenix {
#if defined(Hiro_Desktop)
Size pDesktop::size() {
namespace hiro {
auto pDesktop::size() -> Size {
return {GetSystemMetrics(SM_CXVIRTUALSCREEN), GetSystemMetrics(SM_CYVIRTUALSCREEN)};
}
Geometry pDesktop::workspace() {
auto pDesktop::workspace() -> Geometry {
RECT rc;
SystemParametersInfo(SPI_GETWORKAREA, 0, &rc, 0);
return {rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top};
}
}
#endif

12
hiro/windows/desktop.hpp Normal file
View File

@@ -0,0 +1,12 @@
#if defined(Hiro_Desktop)
namespace hiro {
struct pDesktop {
static auto size() -> Size;
static auto workspace() -> Geometry;
};
}
#endif

View File

@@ -1,34 +1,36 @@
namespace phoenix {
#if defined(Hiro_Font)
string pFont::serif(unsigned size, string style) {
namespace hiro {
auto pFont::serif(unsigned size, string style) -> string {
if(size == 0) size = 8;
if(style == "") style = "Normal";
return {"Georgia, ", size, ", ", style};
}
string pFont::sans(unsigned size, string style) {
auto pFont::sans(unsigned size, string style) -> string {
if(size == 0) size = 8;
if(style == "") style = "Normal";
return {"Tahoma, ", size, ", ", style};
}
string pFont::monospace(unsigned size, string style) {
auto pFont::monospace(unsigned size, string style) -> string {
if(size == 0) size = 8;
if(style == "") style = "Normal";
return {"Lucida Console, ", size, ", ", style};
}
Size pFont::size(string font, string text) {
auto pFont::size(const string& font, const string& text) -> Size {
HFONT hfont = pFont::create(font);
Size size = pFont::size(hfont, text);
pFont::free(hfont);
return size;
}
HFONT pFont::create(string description) {
auto pFont::create(const string& description) -> HFONT {
lstring part = description.split<2>(",").strip();
string family = "Sans";
string family = "Tahoma";
unsigned size = 8u;
bool bold = false;
bool italic = false;
@@ -40,16 +42,16 @@ HFONT pFont::create(string description) {
return CreateFont(
-(size * 96.0 / 72.0 + 0.5),
0, 0, 0, bold == false ? FW_NORMAL : FW_BOLD, italic, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, bold ? FW_BOLD : FW_NORMAL, italic, 0, 0, 0, 0, 0, 0, 0,
utf16_t(family)
);
}
void pFont::free(HFONT hfont) {
auto pFont::free(HFONT hfont) -> void {
DeleteObject(hfont);
}
Size pFont::size(HFONT hfont, string text) {
auto pFont::size(HFONT hfont, string text) -> Size {
//temporary fix: empty text string returns height of zero; bad for eg Button height
if(text.empty()) text = " ";
@@ -62,3 +64,5 @@ Size pFont::size(HFONT hfont, string text) {
}
}
#endif

18
hiro/windows/font.hpp Normal file
View File

@@ -0,0 +1,18 @@
#if defined(Hiro_Font)
namespace hiro {
struct pFont {
static auto serif(unsigned size, string style) -> string;
static auto sans(unsigned size, string style) -> string;
static auto monospace(unsigned size, string style) -> string;
static auto size(const string& font, const string& text) -> Size;
static auto create(const string& description) -> HFONT;
static auto free(HFONT hfont) -> void;
static auto size(HFONT hfont, string text) -> Size;
};
}
#endif

13
hiro/windows/group.cpp Normal file
View File

@@ -0,0 +1,13 @@
#if defined(Hiro_Group)
namespace hiro {
auto pGroup::construct() -> void {
}
auto pGroup::destruct() -> void {
}
}
#endif

11
hiro/windows/group.hpp Normal file
View File

@@ -0,0 +1,11 @@
#if defined(Hiro_Group)
namespace hiro {
struct pGroup : pObject {
Declare(Group, Object)
};
}
#endif

View File

@@ -15,4 +15,42 @@
#include <nall/windows/registry.hpp>
#include <nall/windows/utf8.hpp>
#define TBS_TRANSPARENTBKGND 0x1000
//MinGW/32-bit has painfully outdated platform headers ...
#if !defined(Button_SetImageList)
typedef struct {
HIMAGELIST himl;
RECT margin;
UINT uAlign;
} BUTTON_IMAGELIST, *PBUTTON_IMAGELIST;
#define BUTTON_IMAGELIST_ALIGN_LEFT 0
#define BUTTON_IMAGELIST_ALIGN_RIGHT 1
#define BUTTON_IMAGELIST_ALIGN_TOP 2
#define BUTTON_IMAGELIST_ALIGN_BOTTOM 3
#define BUTTON_IMAGELIST_ALIGN_CENTER 4
#define BCM_FIRST 0x1600
#define BCM_SETIMAGELIST (BCM_FIRST+2)
#define Button_SetImageList(hwnd, pbuttonImagelist) (WINBOOL)SNDMSG((hwnd),BCM_SETIMAGELIST,0,(LPARAM)(pbuttonImagelist))
#endif
#if !defined(BP_CHECKBOX)
#define BP_CHECKBOX 3
#endif
#if !defined(CBS_UNCHECKEDNORMAL)
#define CBS_UNCHECKEDNORMAL 1
#endif
#if !defined(CBS_CHECKEDNORMAL)
#define CBS_CHECKEDNORMAL 5
#endif
#if !defined(LVCFMT_FIXED_WIDTH)
#define LVCFMT_FIXED_WIDTH 0x0100
#endif
#if !defined(TBS_TRANSPARENTBKGND)
#define TBS_TRANSPARENTBKGND 0x1000
#endif

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity type="win32" name="phoenix" version="1.0.0.0" processorArchitecture="*"/>
<assemblyIdentity type="win32" name="hiro" version="1.0.0.0" processorArchitecture="*"/>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>

1
hiro/windows/hiro.rc Normal file
View File

@@ -0,0 +1 @@
1 24 "hiro.Manifest"

13
hiro/windows/hotkey.cpp Normal file
View File

@@ -0,0 +1,13 @@
#if defined(Hiro_Hotkey)
namespace hiro {
auto pHotkey::construct() -> void {
}
auto pHotkey::destruct() -> void {
}
}
#endif

11
hiro/windows/hotkey.hpp Normal file
View File

@@ -0,0 +1,11 @@
#if defined(Hiro_Hotkey)
namespace hiro {
struct pHotkey : pObject {
Declare(Hotkey, Object)
};
}
#endif

View File

@@ -1,144 +1,99 @@
namespace phoenix {
#if defined(Hiro_Keyboard)
void pKeyboard::initialize() {
auto append = [](Keyboard::Scancode scancode, unsigned keysym) {
settings->keymap.insert(scancode, keysym);
};
namespace hiro {
append(Keyboard::Scancode::Escape, VK_ESCAPE);
append(Keyboard::Scancode::F1, VK_F1);
append(Keyboard::Scancode::F2, VK_F2);
append(Keyboard::Scancode::F3, VK_F3);
append(Keyboard::Scancode::F4, VK_F4);
append(Keyboard::Scancode::F5, VK_F5);
append(Keyboard::Scancode::F6, VK_F6);
append(Keyboard::Scancode::F7, VK_F7);
append(Keyboard::Scancode::F8, VK_F8);
append(Keyboard::Scancode::F9, VK_F9);
append(Keyboard::Scancode::F10, VK_F10);
append(Keyboard::Scancode::F11, VK_F11);
append(Keyboard::Scancode::F12, VK_F12);
vector<uint16_t> pKeyboard::keycodes;
append(Keyboard::Scancode::PrintScreen, VK_SNAPSHOT);
append(Keyboard::Scancode::ScrollLock, VK_SCROLL);
append(Keyboard::Scancode::Pause, VK_PAUSE);
append(Keyboard::Scancode::Insert, VK_INSERT);
append(Keyboard::Scancode::Delete, VK_DELETE);
append(Keyboard::Scancode::Home, VK_HOME);
append(Keyboard::Scancode::End, VK_END);
append(Keyboard::Scancode::PageUp, VK_PRIOR);
append(Keyboard::Scancode::PageDown, VK_NEXT);
append(Keyboard::Scancode::Up, VK_UP);
append(Keyboard::Scancode::Down, VK_DOWN);
append(Keyboard::Scancode::Left, VK_LEFT);
append(Keyboard::Scancode::Right, VK_RIGHT);
append(Keyboard::Scancode::Grave, VK_OEM_3);
append(Keyboard::Scancode::Number1, '1');
append(Keyboard::Scancode::Number2, '2');
append(Keyboard::Scancode::Number3, '3');
append(Keyboard::Scancode::Number4, '4');
append(Keyboard::Scancode::Number5, '5');
append(Keyboard::Scancode::Number6, '6');
append(Keyboard::Scancode::Number7, '7');
append(Keyboard::Scancode::Number8, '8');
append(Keyboard::Scancode::Number9, '9');
append(Keyboard::Scancode::Number0, '0');
append(Keyboard::Scancode::Minus, VK_OEM_MINUS);
append(Keyboard::Scancode::Equal, VK_OEM_PLUS);
append(Keyboard::Scancode::Backspace, VK_BACK);
append(Keyboard::Scancode::BracketLeft, VK_OEM_4);
append(Keyboard::Scancode::BracketRight, VK_OEM_6);
append(Keyboard::Scancode::Backslash, VK_OEM_5);
append(Keyboard::Scancode::Semicolon, VK_OEM_1);
append(Keyboard::Scancode::Apostrophe, VK_OEM_7);
append(Keyboard::Scancode::Comma, VK_OEM_COMMA);
append(Keyboard::Scancode::Period, VK_OEM_PERIOD);
append(Keyboard::Scancode::Slash, VK_OEM_2);
append(Keyboard::Scancode::Tab, VK_TAB);
append(Keyboard::Scancode::CapsLock, VK_CAPITAL);
append(Keyboard::Scancode::Return, VK_RETURN);
append(Keyboard::Scancode::ShiftLeft, VK_LSHIFT);
append(Keyboard::Scancode::ShiftRight, VK_RSHIFT);
append(Keyboard::Scancode::ControlLeft, VK_LCONTROL);
append(Keyboard::Scancode::ControlRight, VK_RCONTROL);
append(Keyboard::Scancode::SuperLeft, VK_LWIN);
append(Keyboard::Scancode::SuperRight, VK_RWIN);
append(Keyboard::Scancode::AltLeft, VK_LMENU);
append(Keyboard::Scancode::AltRight, VK_RMENU);
append(Keyboard::Scancode::Space, VK_SPACE);
append(Keyboard::Scancode::Menu, VK_APPS);
append(Keyboard::Scancode::A, 'A');
append(Keyboard::Scancode::B, 'B');
append(Keyboard::Scancode::C, 'C');
append(Keyboard::Scancode::D, 'D');
append(Keyboard::Scancode::E, 'E');
append(Keyboard::Scancode::F, 'F');
append(Keyboard::Scancode::G, 'G');
append(Keyboard::Scancode::H, 'H');
append(Keyboard::Scancode::I, 'I');
append(Keyboard::Scancode::J, 'J');
append(Keyboard::Scancode::K, 'K');
append(Keyboard::Scancode::L, 'L');
append(Keyboard::Scancode::M, 'M');
append(Keyboard::Scancode::N, 'N');
append(Keyboard::Scancode::O, 'O');
append(Keyboard::Scancode::P, 'P');
append(Keyboard::Scancode::Q, 'Q');
append(Keyboard::Scancode::R, 'R');
append(Keyboard::Scancode::S, 'S');
append(Keyboard::Scancode::T, 'T');
append(Keyboard::Scancode::U, 'U');
append(Keyboard::Scancode::V, 'V');
append(Keyboard::Scancode::W, 'W');
append(Keyboard::Scancode::X, 'X');
append(Keyboard::Scancode::Y, 'Y');
append(Keyboard::Scancode::Z, 'Z');
append(Keyboard::Scancode::NumLock, VK_NUMLOCK);
append(Keyboard::Scancode::Divide, VK_DIVIDE);
append(Keyboard::Scancode::Multiply, VK_MULTIPLY);
append(Keyboard::Scancode::Subtract, VK_SUBTRACT);
append(Keyboard::Scancode::Add, VK_ADD);
//append(Keyboard::Scancode::Enter, ...);
append(Keyboard::Scancode::Point, VK_DECIMAL);
append(Keyboard::Scancode::Keypad1, VK_NUMPAD1);
append(Keyboard::Scancode::Keypad2, VK_NUMPAD2);
append(Keyboard::Scancode::Keypad3, VK_NUMPAD3);
append(Keyboard::Scancode::Keypad4, VK_NUMPAD4);
append(Keyboard::Scancode::Keypad5, VK_NUMPAD5);
append(Keyboard::Scancode::Keypad6, VK_NUMPAD6);
append(Keyboard::Scancode::Keypad7, VK_NUMPAD7);
append(Keyboard::Scancode::Keypad8, VK_NUMPAD8);
append(Keyboard::Scancode::Keypad9, VK_NUMPAD9);
append(Keyboard::Scancode::Keypad0, VK_NUMPAD0);
auto pKeyboard::poll() -> vector<bool> {
vector<bool> result;
for(auto& code : keycodes) result.append(pressed(code));
return result;
}
bool pKeyboard::pressed(Keyboard::Scancode scancode) {
if(auto result = settings->keymap.find(scancode)) {
return GetAsyncKeyState(result()) & 0x8000;
}
auto pKeyboard::pressed(unsigned code) -> bool {
uint8_t lo = code >> 0;
uint8_t hi = code >> 8;
if(lo && GetAsyncKeyState(lo) & 0x8000) return true;
if(hi && GetAsyncKeyState(hi) & 0x8000) return true;
return false;
}
vector<bool> pKeyboard::state() {
vector<bool> output;
output.resize((unsigned)Keyboard::Scancode::Limit);
for(auto& n : output) n = false;
auto pKeyboard::initialize() -> void {
auto append = [](unsigned lo, unsigned hi = 0) {
keycodes.append(lo << 0 | hi << 8);
};
for(auto node : settings->keymap) {
if(GetAsyncKeyState(node.value) & 0x8000) {
output[(unsigned)node.key] = true;
}
#define map(name, ...) if(key == name) { append(__VA_ARGS__); continue; }
for(auto& key : Keyboard::keys) {
#include <hiro/platform/windows/keyboard.hpp>
//print("[hiro/windows] warning: unhandled key: ", key, "\n");
append(0);
}
#undef map
}
auto pKeyboard::_translate(unsigned code, unsigned flags) -> signed {
bool numLock = GetKeyState(VK_NUMLOCK);
bool capsLock = GetKeyState(VK_CAPITAL);
bool shifted = (GetAsyncKeyState(VK_LSHIFT) & 0x8000) || (GetAsyncKeyState(VK_RSHIFT) & 0x8000);
bool pressed = GetAsyncKeyState(code) & 0x8000;
bool extended = flags & (1 << 24);
switch(code) {
case VK_OEM_3: return !shifted ? '`' : '~';
case '1': return !shifted ? '1' : '!';
case '2': return !shifted ? '2' : '@';
case '3': return !shifted ? '3' : '#';
case '4': return !shifted ? '4' : '$';
case '5': return !shifted ? '5' : '%';
case '6': return !shifted ? '6' : '^';
case '7': return !shifted ? '7' : '&';
case '8': return !shifted ? '8' : '*';
case '9': return !shifted ? '9' : '(';
case '0': return !shifted ? '0' : ')';
case VK_OEM_MINUS: return !shifted ? '-' : '_';
case VK_OEM_PLUS: return !shifted ? '=' : '+';
case VK_BACK: return '\b';
case VK_TAB: return '\t';
case VK_RETURN: return '\n';
case VK_SPACE: return ' ';
case VK_OEM_4: return !shifted ? '[' : '{';
case VK_OEM_6: return !shifted ? ']' : '}';
case VK_OEM_5: return !shifted ? '\\' : '|';
case VK_OEM_1: return !shifted ? ';' : ':';
case VK_OEM_7: return !shifted ? '\'' : '\"';
case VK_OEM_COMMA: return !shifted ? ',' : '<';
case VK_OEM_PERIOD: return !shifted ? '.' : '>';
case VK_OEM_2: return !shifted ? '/' : '?';
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M':
case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z':
if(capsLock) return !shifted ? code : code + 32;
else return !shifted ? code + 32 : code;
case VK_DIVIDE: return '/';
case VK_MULTIPLY: return '*';
case VK_SUBTRACT: return '-';
case VK_ADD: return '+';
case VK_DECIMAL: return '.';
case VK_NUMPAD1: return numLock ? '1' : 0;
case VK_NUMPAD2: return numLock ? '2' : 0;
case VK_NUMPAD3: return numLock ? '3' : 0;
case VK_NUMPAD4: return numLock ? '4' : 0;
case VK_NUMPAD5: return numLock ? '5' : 0;
case VK_NUMPAD6: return numLock ? '6' : 0;
case VK_NUMPAD7: return numLock ? '7' : 0;
case VK_NUMPAD8: return numLock ? '8' : 0;
case VK_NUMPAD9: return numLock ? '9' : 0;
case VK_NUMPAD0: return numLock ? '0' : 0;
}
return output;
return 0;
}
}
#endif

18
hiro/windows/keyboard.hpp Normal file
View File

@@ -0,0 +1,18 @@
#if defined(Hiro_Keyboard)
namespace hiro {
struct pKeyboard {
static auto poll() -> vector<bool>;
static auto pressed(unsigned code) -> bool;
static auto initialize() -> void;
static auto _translate(unsigned code, unsigned flags) -> signed;
static vector<uint16_t> keycodes;
};
}
#endif

22
hiro/windows/layout.cpp Normal file
View File

@@ -0,0 +1,22 @@
#if defined(Hiro_Layout)
namespace hiro {
auto pLayout::construct() -> void {
}
auto pLayout::destruct() -> void {
}
auto pLayout::setEnabled(bool enabled) -> void {
}
auto pLayout::setFont(const string& font) -> void {
}
auto pLayout::setVisible(bool visible) -> void {
}
}
#endif

15
hiro/windows/layout.hpp Normal file
View File

@@ -0,0 +1,15 @@
#if defined(Hiro_Layout)
namespace hiro {
struct pLayout : pSizable {
Declare(Layout, Sizable)
auto setEnabled(bool enabled) -> void override;
auto setFont(const string& font) -> void override;
auto setVisible(bool visible) -> void override;
};
}
#endif

83
hiro/windows/menu-bar.cpp Normal file
View File

@@ -0,0 +1,83 @@
#if defined(Hiro_MenuBar)
namespace hiro {
auto pMenuBar::construct() -> void {
_update();
}
auto pMenuBar::destruct() -> void {
if(hmenu) { DestroyMenu(hmenu); hmenu = nullptr; }
if(auto parent = _parent()) {
SetMenu(parent->hwnd, nullptr);
}
}
auto pMenuBar::append(sMenu) -> void {
_update();
}
auto pMenuBar::remove(sMenu) -> void {
_update();
}
auto pMenuBar::setEnabled(bool enabled) -> void {
_update();
}
auto pMenuBar::setFont(const string& font) -> void {
//unsupported
}
auto pMenuBar::setVisible(bool visible) -> void {
if(auto parent = _parent()) {
SetMenu(parent->hwnd, visible ? hmenu : nullptr);
parent->setGeometry(parent->state().geometry);
}
}
auto pMenuBar::_parent() -> maybe<pWindow&> {
if(auto parent = self().parentWindow(true)) {
if(auto self = parent->self()) return *self;
}
return nothing;
}
auto pMenuBar::_update() -> void {
if(hmenu) DestroyMenu(hmenu);
hmenu = CreateMenu();
MENUINFO mi{sizeof(MENUINFO)};
mi.fMask = MIM_STYLE;
mi.dwStyle = MNS_NOTIFYBYPOS; //| MNS_MODELESS;
SetMenuInfo(hmenu, &mi);
unsigned position = 0;
#if defined(Hiro_Menu)
for(auto& menu : state().menus) {
unsigned enabled = menu->enabled() ? 0 : MF_GRAYED;
MENUITEMINFO mii{sizeof(MENUITEMINFO)};
mii.fMask = MIIM_DATA;
mii.dwItemData = (ULONG_PTR)menu.data();
if(menu->visible()) {
if(auto self = menu->self()) {
self->_update();
AppendMenu(hmenu, MF_STRING | MF_POPUP | enabled, (UINT_PTR)self->hmenu, utf16_t(menu->text()));
SetMenuItemInfo(hmenu, position++, true, &mii);
}
}
}
#endif
if(auto parent = _parent()) {
SetMenu(parent->hwnd, self().visible(true) ? hmenu : nullptr);
parent->setGeometry(parent->state().geometry);
}
}
}
#endif

23
hiro/windows/menu-bar.hpp Normal file
View File

@@ -0,0 +1,23 @@
#if defined(Hiro_MenuBar)
namespace hiro {
struct pMenuBar : pObject {
Declare(MenuBar, Object)
auto append(sMenu menu) -> void;
auto remove(sMenu menu) -> void;
auto setEnabled(bool enabled) -> void override;
auto setFont(const string& font) -> void override;
auto setVisible(bool visible) -> void override;
auto _parent() -> maybe<pWindow&>;
auto _update() -> void;
HMENU hmenu = 0;
vector<wObject> objects;
};
}
#endif

View File

@@ -1,6 +1,8 @@
namespace phoenix {
#if defined(Hiro_MessageWindow)
static MessageWindow::Response MessageWindow_response(MessageWindow::Buttons buttons, UINT response) {
namespace hiro {
static auto MessageWindow_response(MessageWindow::Buttons buttons, UINT response) -> MessageWindow::Response {
if(response == IDOK) return MessageWindow::Response::Ok;
if(response == IDCANCEL) return MessageWindow::Response::Cancel;
if(response == IDYES) return MessageWindow::Response::Yes;
@@ -15,7 +17,7 @@ static MessageWindow::Response MessageWindow_response(MessageWindow::Buttons but
throw;
}
static UINT MessageWindow_buttons(MessageWindow::Buttons buttons) {
static auto MessageWindow_buttons(MessageWindow::Buttons buttons) -> UINT {
if(buttons == MessageWindow::Buttons::Ok) return MB_OK;
if(buttons == MessageWindow::Buttons::OkCancel) return MB_OKCANCEL;
if(buttons == MessageWindow::Buttons::YesNo) return MB_YESNO;
@@ -23,32 +25,34 @@ static UINT MessageWindow_buttons(MessageWindow::Buttons buttons) {
throw;
}
MessageWindow::Response pMessageWindow::error(MessageWindow::State& state) {
auto pMessageWindow::error(MessageWindow::State& state) -> MessageWindow::Response {
UINT flags = MB_ICONERROR | MessageWindow_buttons(state.buttons);
return MessageWindow_response(state.buttons, MessageBox(
state.parent ? state.parent->p.hwnd : 0, utf16_t(state.text), utf16_t(state.title), flags
state.parent ? state.parent->self()->hwnd : 0, utf16_t(state.text), utf16_t(state.title), flags
));
}
MessageWindow::Response pMessageWindow::information(MessageWindow::State& state) {
auto pMessageWindow::information(MessageWindow::State& state) -> MessageWindow::Response {
UINT flags = MB_ICONINFORMATION | MessageWindow_buttons(state.buttons);
return MessageWindow_response(state.buttons, MessageBox(
state.parent ? state.parent->p.hwnd : 0, utf16_t(state.text), utf16_t(state.title), flags
state.parent ? state.parent->self()->hwnd : 0, utf16_t(state.text), utf16_t(state.title), flags
));
}
MessageWindow::Response pMessageWindow::question(MessageWindow::State& state) {
auto pMessageWindow::question(MessageWindow::State& state) -> MessageWindow::Response {
UINT flags = MB_ICONQUESTION | MessageWindow_buttons(state.buttons);
return MessageWindow_response(state.buttons, MessageBox(
state.parent ? state.parent->p.hwnd : 0, utf16_t(state.text), utf16_t(state.title), flags
state.parent ? state.parent->self()->hwnd : 0, utf16_t(state.text), utf16_t(state.title), flags
));
}
MessageWindow::Response pMessageWindow::warning(MessageWindow::State& state) {
auto pMessageWindow::warning(MessageWindow::State& state) -> MessageWindow::Response {
UINT flags = MB_ICONWARNING | MessageWindow_buttons(state.buttons);
return MessageWindow_response(state.buttons, MessageBox(
state.parent ? state.parent->p.hwnd : 0, utf16_t(state.text), utf16_t(state.title), flags
state.parent ? state.parent->self()->hwnd : 0, utf16_t(state.text), utf16_t(state.title), flags
));
}
}
#endif

View File

@@ -0,0 +1,14 @@
#if defined(Hiro_MessageWindow)
namespace hiro {
struct pMessageWindow {
static auto error(MessageWindow::State& state) -> MessageWindow::Response;
static auto information(MessageWindow::State& state) -> MessageWindow::Response;
static auto question(MessageWindow::State& state) -> MessageWindow::Response;
static auto warning(MessageWindow::State& state) -> MessageWindow::Response;
};
}
#endif

View File

@@ -1,4 +1,6 @@
namespace phoenix {
#if defined(Hiro_Monitor)
namespace hiro {
struct MonitorInfo {
unsigned monitor = 0;
@@ -7,7 +9,7 @@ struct MonitorInfo {
Geometry geometry;
};
static BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
static auto CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) -> BOOL {
MonitorInfo& info = *(MonitorInfo*)dwData;
MONITORINFOEX mi;
memset(&mi, 0, sizeof(MONITORINFOEX));
@@ -23,21 +25,23 @@ static BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT l
return TRUE;
}
unsigned pMonitor::count() {
auto pMonitor::count() -> unsigned {
return GetSystemMetrics(SM_CMONITORS);
}
Geometry pMonitor::geometry(unsigned monitor) {
auto pMonitor::geometry(unsigned monitor) -> Geometry {
MonitorInfo info;
info.monitor = monitor;
EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (LPARAM)&info);
return info.geometry;
}
unsigned pMonitor::primary() {
auto pMonitor::primary() -> unsigned {
MonitorInfo info;
EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (LPARAM)&info);
return info.primary;
}
}
#endif

13
hiro/windows/monitor.hpp Normal file
View File

@@ -0,0 +1,13 @@
#if defined(Hiro_Monitor)
namespace hiro {
struct pMonitor {
static auto count() -> unsigned;
static auto geometry(unsigned monitor) -> Geometry;
static auto primary() -> unsigned;
};
}
#endif

View File

@@ -1,12 +1,14 @@
namespace phoenix {
#if defined(Hiro_Mouse)
Position pMouse::position() {
POINT point = {0};
namespace hiro {
auto pMouse::position() -> Position {
POINT point{0};
GetCursorPos(&point);
return {point.x, point.y};
}
bool pMouse::pressed(Mouse::Button button) {
auto pMouse::pressed(Mouse::Button button) -> bool {
switch(button) {
case Mouse::Button::Left: return GetAsyncKeyState(VK_LBUTTON) & 0x8000;
case Mouse::Button::Middle: return GetAsyncKeyState(VK_MBUTTON) & 0x8000;
@@ -16,3 +18,5 @@ bool pMouse::pressed(Mouse::Button button) {
}
}
#endif

12
hiro/windows/mouse.hpp Normal file
View File

@@ -0,0 +1,12 @@
#if defined(Hiro_Mouse)
namespace hiro {
struct pMouse {
static auto position() -> Position;
static auto pressed(Mouse::Button button) -> bool;
};
}
#endif

View File

@@ -1,17 +1,41 @@
namespace phoenix {
#if defined(Hiro_Object)
vector<pObject*> pObject::objects;
namespace hiro {
pObject::pObject(Object& object) : object(object) {
static unsigned uniqueId = 100;
objects.append(this);
id = uniqueId++;
locked = false;
auto pObject::construct() -> void {
}
Object* pObject::find(unsigned id) {
for(auto& item : objects) if(item->id == id) return &item->object;
return nullptr;
auto pObject::destruct() -> void {
}
auto pObject::reconstruct() -> void {
}
auto pObject::focused() const -> bool {
return false;
}
auto pObject::remove() -> void {
}
auto pObject::reset() -> void {
}
auto pObject::setEnabled(bool enabled) -> void {
}
auto pObject::setFocused() -> void {
}
auto pObject::setFont(const string& font) -> void {
}
auto pObject::setGroup(sGroup group) -> void {
}
auto pObject::setVisible(bool visible) -> void {
}
}
#endif

31
hiro/windows/object.hpp Normal file
View File

@@ -0,0 +1,31 @@
#if defined(Hiro_Object)
namespace hiro {
struct pObject {
pObject(mObject& reference) : reference(reference) {}
virtual ~pObject() = default;
virtual auto construct() -> void;
virtual auto destruct() -> void;
virtual auto reconstruct() -> void;
virtual auto focused() const -> bool;
virtual auto remove() -> void;
virtual auto reset() -> void;
virtual auto setEnabled(bool enabled) -> void;
virtual auto setFocused() -> void;
virtual auto setFont(const string& font) -> void;
virtual auto setGroup(sGroup group) -> void;
virtual auto setVisible(bool visible) -> void;
auto locked() const -> bool { return locks != 0; }
auto lock() -> void { ++locks; }
auto unlock() -> void { --locks; }
mObject& reference;
signed locks = 0;
};
}
#endif

View File

@@ -1 +0,0 @@
1 24 "phoenix.Manifest"

View File

@@ -1,7 +1,5 @@
#include "platform.hpp"
#include "utility.cpp"
#include "settings.cpp"
#include "desktop.cpp"
#include "monitor.cpp"
@@ -9,17 +7,27 @@
#include "mouse.cpp"
#include "browser-window.cpp"
#include "message-window.cpp"
#include "object.cpp"
#include "group.cpp"
#include "hotkey.cpp"
#include "font.cpp"
#include "timer.cpp"
#include "window.cpp"
#include "status-bar.cpp"
#include "menu-bar.cpp"
#include "popup-menu.cpp"
#include "action/action.cpp"
#include "action/menu.cpp"
#include "action/separator.cpp"
#include "action/item.cpp"
#include "action/check-item.cpp"
#include "action/radio-item.cpp"
#include "action/menu-separator.cpp"
#include "action/menu-item.cpp"
#include "action/menu-check-item.cpp"
#include "action/menu-radio-item.cpp"
#include "sizable.cpp"
#include "layout.cpp"
#include "widget/widget.cpp"
#include "widget/button.cpp"
@@ -27,7 +35,7 @@
#include "widget/check-button.cpp"
#include "widget/check-label.cpp"
#include "widget/combo-button.cpp"
#include "widget/console.cpp"
#include "widget/combo-button-item.cpp"
#include "widget/frame.cpp"
#include "widget/hex-edit.cpp"
#include "widget/horizontal-scroller.cpp"
@@ -35,10 +43,14 @@
#include "widget/label.cpp"
#include "widget/line-edit.cpp"
#include "widget/list-view.cpp"
#include "widget/list-view-column.cpp"
#include "widget/list-view-item.cpp"
#include "widget/list-view-cell.cpp"
#include "widget/progress-bar.cpp"
#include "widget/radio-button.cpp"
#include "widget/radio-label.cpp"
#include "widget/tab-frame.cpp"
#include "widget/tab-frame-item.cpp"
#include "widget/text-edit.cpp"
#include "widget/vertical-scroller.cpp"
#include "widget/vertical-slider.cpp"

View File

@@ -1,25 +1,4 @@
namespace phoenix {
struct AppMessage {
enum : unsigned {
ListView_onActivate = 0,
};
};
typedef LRESULT CALLBACK (*WindowProc)(HWND, UINT, WPARAM, LPARAM);
struct pApplication {
static void run();
static bool pendingEvents();
static void processEvents();
static void quit();
static void initialize();
};
struct Settings {
bimap<Keyboard::Scancode, unsigned> keymap;
};
namespace hiro {
struct pFont;
struct pObject;
@@ -28,620 +7,81 @@ struct pMenu;
struct pLayout;
struct pWidget;
struct pFont {
static string serif(unsigned size, string style);
static string sans(unsigned size, string style);
static string monospace(unsigned size, string style);
static Size size(string font, string text);
static HFONT create(string description);
static void free(HFONT hfont);
static Size size(HFONT hfont, string text);
struct AppMessage {
enum : unsigned {
None = WM_APP,
ListView_onActivate,
ListView_onChange,
};
};
struct pDesktop {
static Size size();
static Geometry workspace();
};
struct pMonitor {
static unsigned count();
static Geometry geometry(unsigned monitor);
static unsigned primary();
};
struct pKeyboard {
static bool pressed(Keyboard::Scancode scancode);
static vector<bool> state();
static void initialize();
};
struct pMouse {
static Position position();
static bool pressed(Mouse::Button button);
};
struct pBrowserWindow {
static string directory(BrowserWindow::State& state);
static string open(BrowserWindow::State& state);
static string save(BrowserWindow::State& state);
};
struct pMessageWindow {
static MessageWindow::Response error(MessageWindow::State& state);
static MessageWindow::Response information(MessageWindow::State& state);
static MessageWindow::Response question(MessageWindow::State& state);
static MessageWindow::Response warning(MessageWindow::State& state);
};
struct pObject {
static vector<pObject*> objects;
Object& object;
uintptr_t id;
bool locked;
pObject(Object& object);
static Object* find(unsigned id);
virtual ~pObject() {}
void constructor() {}
void destructor() {}
};
struct pTimer : public pObject {
Timer& timer;
UINT_PTR htimer;
void setEnabled(bool enabled);
void setInterval(unsigned interval);
pTimer(Timer& timer) : pObject(timer), timer(timer) {}
void constructor();
};
struct pWindow : public pObject {
static vector<pWindow*> modal;
static void updateModality();
Window& window;
HWND hwnd;
HMENU hmenu;
HWND hstatus;
HFONT hstatusfont;
HBRUSH brush;
COLORREF brushColor;
static Window& none();
void append(Layout& layout);
void append(Menu& menu);
void append(Widget& widget);
bool focused();
Geometry frameMargin();
Geometry geometry();
void remove(Layout& layout);
void remove(Menu& menu);
void remove(Widget& widget);
void setBackgroundColor(Color color);
void setDroppable(bool droppable);
void setFocused();
void setFullScreen(bool fullScreen);
void setGeometry(Geometry geometry);
void setMenuFont(string font);
void setMenuVisible(bool visible);
void setModal(bool modal);
void setResizable(bool resizable);
void setStatusFont(string font);
void setStatusText(string text);
void setStatusVisible(bool visible);
void setTitle(string text);
void setVisible(bool visible);
void setWidgetFont(string font);
pWindow(Window& window) : pObject(window), window(window) {}
void constructor();
void destructor();
void updateMenu();
void onClose();
void onDrop(WPARAM wparam);
bool onEraseBackground();
void onModalBegin();
void onModalEnd();
void onMove();
void onSize();
};
struct pAction : public pObject {
Action& action;
Menu* parentMenu;
Window* parentWindow;
void setEnabled(bool enabled);
void setVisible(bool visible);
pAction(Action& action) : pObject(action), action(action) {}
void constructor();
};
struct pMenu : public pAction {
Menu& menu;
HMENU hmenu;
HBITMAP hbitmap;
void append(Action& action);
void remove(Action& action);
void setImage(const image& image);
void setText(string text);
pMenu(Menu& menu) : pAction(menu), menu(menu), hbitmap(0) {}
void constructor();
void destructor();
void createBitmap();
void update(Window& parentWindow, Menu* parentMenu = nullptr);
};
struct pSeparator : public pAction {
Separator& separator;
pSeparator(Separator& separator) : pAction(separator), separator(separator) {}
void constructor();
void destructor();
};
struct pItem : public pAction {
Item& item;
HBITMAP hbitmap;
void setImage(const image& image);
void setText(string text);
pItem(Item& item) : pAction(item), item(item), hbitmap(0) {}
void constructor();
void destructor();
void createBitmap();
void onActivate();
};
struct pCheckItem : public pAction {
CheckItem& checkItem;
void setChecked(bool checked);
void setText(string text);
pCheckItem(CheckItem& checkItem) : pAction(checkItem), checkItem(checkItem) {}
void constructor();
void destructor();
void onToggle();
};
struct pRadioItem : public pAction {
RadioItem& radioItem;
void setChecked();
void setGroup(const group<RadioItem>& group);
void setText(string text);
pRadioItem(RadioItem& radioItem) : pAction(radioItem), radioItem(radioItem) {}
void constructor();
void destructor();
void onActivate();
};
struct pSizable : public pObject {
Sizable& sizable;
pSizable(Sizable& sizable) : pObject(sizable), sizable(sizable) {}
};
struct pLayout : public pSizable {
Layout& layout;
pLayout(Layout& layout) : pSizable(layout), layout(layout) {}
};
struct pWidget : public pSizable {
Widget& widget;
HWND parentHwnd;
HWND hwnd;
HFONT hfont;
bool focused();
virtual Size minimumSize();
virtual void setEnabled(bool enabled);
void setFocused();
void setFont(string font);
virtual void setGeometry(Geometry geometry);
virtual void setVisible(bool visible);
pWidget(Widget& widget) : pSizable(widget), widget(widget) { parentHwnd = pWindow::none().p.hwnd; }
void constructor();
void destructor();
virtual void orphan();
void setDefaultFont();
void synchronize();
};
struct pButton : public pWidget {
Button& button;
HBITMAP hbitmap;
HIMAGELIST himagelist;
Size minimumSize();
void setBordered(bool bordered);
void setImage(const image& image, Orientation orientation);
void setText(string text);
pButton(Button& button) : pWidget(button), button(button), hbitmap(0), himagelist(0) {}
void constructor();
void destructor();
void orphan();
void onActivate();
};
struct pCanvas : public pWidget {
Canvas& canvas;
uint32_t* surface = nullptr;
unsigned surfaceWidth = 0;
unsigned surfaceHeight = 0;
void setDroppable(bool droppable);
void setGeometry(Geometry geometry);
void setMode(Canvas::Mode mode);
void setSize(Size size);
pCanvas(Canvas& canvas) : pWidget(canvas), canvas(canvas) {}
void constructor();
void destructor();
void orphan();
void paint();
void rasterize();
void redraw();
void release();
};
struct pCheckButton : public pWidget {
CheckButton& checkButton;
HBITMAP hbitmap;
HIMAGELIST himagelist;
Size minimumSize();
void setChecked(bool checked);
void setImage(const image& image, Orientation orientation);
void setText(string text);
pCheckButton(CheckButton& checkButton) : pWidget(checkButton), checkButton(checkButton) {}
void constructor();
void destructor();
void orphan();
void onToggle();
};
struct pCheckLabel : public pWidget {
CheckLabel& checkLabel;
Size minimumSize();
void setChecked(bool checked);
void setText(string text);
pCheckLabel(CheckLabel& checkLabel) : pWidget(checkLabel), checkLabel(checkLabel) {}
void constructor();
void destructor();
void orphan();
void onToggle();
};
struct pComboButton : public pWidget {
ComboButton& comboButton;
void append(string text);
void remove(unsigned selection);
Size minimumSize();
void reset();
void setGeometry(Geometry geometry);
void setSelection(unsigned selection);
void setText(unsigned selection, string text);
pComboButton(ComboButton& comboButton) : pWidget(comboButton), comboButton(comboButton) {}
void constructor();
void destructor();
void orphan();
void onChange();
};
struct pConsole : public pWidget {
Console& console;
LRESULT CALLBACK (*windowProc)(HWND, UINT, LPARAM, WPARAM);
HBRUSH backgroundBrush = nullptr;
void print(string text);
void reset();
void setBackgroundColor(Color color);
void setForegroundColor(Color color);
void setPrompt(string prompt);
pConsole(Console& console) : pWidget(console), console(console) {}
void constructor();
void destructor();
void orphan();
bool keyPress(unsigned key);
};
struct pFrame : public pWidget {
Frame& frame;
void setEnabled(bool enabled);
void setGeometry(Geometry geometry);
void setText(string text);
void setVisible(bool visible);
pFrame(Frame& frame) : pWidget(frame), frame(frame) {}
void constructor();
void destructor();
void orphan();
};
struct pHexEdit : public pWidget {
HexEdit& hexEdit;
WindowProc windowProc = nullptr;
HWND scrollBar = nullptr;
HBRUSH backgroundBrush = nullptr;
void setBackgroundColor(Color color);
void setColumns(unsigned columns);
void setForegroundColor(Color color);
void setLength(unsigned length);
void setOffset(unsigned offset);
void setRows(unsigned rows);
void update();
pHexEdit(HexEdit& hexEdit) : pWidget(hexEdit), hexEdit(hexEdit) {}
void constructor();
void destructor();
void orphan();
bool keyPress(unsigned key);
signed rows();
signed rowsScrollable();
signed scrollPosition();
void scrollTo(signed position);
};
struct pHorizontalScroller : public pWidget {
HorizontalScroller& horizontalScroller;
Size minimumSize();
void setLength(unsigned length);
void setPosition(unsigned position);
pHorizontalScroller(HorizontalScroller& horizontalScroller) : pWidget(horizontalScroller), horizontalScroller(horizontalScroller) {}
void constructor();
void destructor();
void orphan();
void onChange(WPARAM wparam);
};
struct pHorizontalSlider : public pWidget {
HorizontalSlider& horizontalSlider;
Size minimumSize();
void setLength(unsigned length);
void setPosition(unsigned position);
pHorizontalSlider(HorizontalSlider& horizontalSlider) : pWidget(horizontalSlider), horizontalSlider(horizontalSlider) {}
void constructor();
void destructor();
void orphan();
void onChange();
};
struct pLabel : public pWidget {
Label& label;
Size minimumSize();
void setText(string text);
pLabel(Label& label) : pWidget(label), label(label) {}
void constructor();
void destructor();
void orphan();
};
struct pLineEdit : public pWidget {
LineEdit& lineEdit;
HBRUSH backgroundBrush = nullptr;
Size minimumSize();
void setBackgroundColor(Color color);
void setEditable(bool editable);
void setForegroundColor(Color color);
void setText(string text);
string text();
pLineEdit(LineEdit& lineEdit) : pWidget(lineEdit), lineEdit(lineEdit) {}
void constructor();
void destructor();
void orphan();
void onChange();
};
struct pListView : public pWidget {
ListView& listView;
HIMAGELIST imageList;
vector<vector<unsigned>> imageMap;
vector<image> images;
bool lostFocus;
void append(const lstring& text);
void autoSizeColumns();
void remove(unsigned selection);
void reset();
void setBackgroundColor(Color color);
void setCheckable(bool checkable);
void setChecked(unsigned selection, bool checked);
void setForegroundColor(Color color);
void setGeometry(Geometry geometry);
void setHeaderText(const lstring& text);
void setHeaderVisible(bool visible);
void setImage(unsigned selection, unsigned position, const image& image);
void setSelected(bool selected);
void setSelection(unsigned selection);
void setText(unsigned selection, unsigned position, string text);
pListView(ListView& listView) : pWidget(listView), listView(listView), imageList(nullptr) {}
void constructor();
void destructor();
void orphan();
void buildImageList();
void onActivate(LPARAM lparam);
void onChange(LPARAM lparam);
LRESULT onCustomDraw(LPARAM lparam);
};
struct pProgressBar : public pWidget {
ProgressBar& progressBar;
Size minimumSize();
void setPosition(unsigned position);
pProgressBar(ProgressBar& progressBar) : pWidget(progressBar), progressBar(progressBar) {}
void constructor();
void destructor();
void orphan();
};
struct pRadioButton : public pWidget {
RadioButton& radioButton;
HBITMAP hbitmap;
HIMAGELIST himagelist;
Size minimumSize();
void setChecked();
void setGroup(const group<RadioButton>& group);
void setImage(const image& image, Orientation orientation);
void setText(string text);
pRadioButton(RadioButton& radioButton) : pWidget(radioButton), radioButton(radioButton) {}
void constructor();
void destructor();
void orphan();
void onActivate();
};
struct pRadioLabel : public pWidget {
RadioLabel& radioLabel;
Size minimumSize();
void setChecked();
void setGroup(const group<RadioLabel>& group);
void setText(string text);
pRadioLabel(RadioLabel& radioLabel) : pWidget(radioLabel), radioLabel(radioLabel) {}
void constructor();
void destructor();
void orphan();
void onActivate();
};
struct pTabFrame : public pWidget {
TabFrame& tabFrame;
WindowProc windowProc = nullptr;
HIMAGELIST imageList = nullptr;
void append(string text, const image& image);
void remove(unsigned selection);
void setEnabled(bool enabled);
void setGeometry(Geometry geometry);
void setImage(unsigned selection, const image& image);
void setSelection(unsigned selection);
void setText(unsigned selection, string text);
void setVisible(bool visible);
pTabFrame(TabFrame& tabFrame) : pWidget(tabFrame), tabFrame(tabFrame) {}
void constructor();
void destructor();
void orphan();
void buildImageList();
void synchronizeLayout();
void onChange();
void onDrawItem(LPARAM lparam);
};
struct pTextEdit : public pWidget {
TextEdit& textEdit;
HBRUSH backgroundBrush = nullptr;
void setBackgroundColor(Color color);
void setCursorPosition(unsigned position);
void setEditable(bool editable);
void setForegroundColor(Color color);
void setText(string text);
void setWordWrap(bool wordWrap);
string text();
pTextEdit(TextEdit& textEdit) : pWidget(textEdit), textEdit(textEdit) {}
void constructor();
void destructor();
void orphan();
void onChange();
};
struct pVerticalScroller : public pWidget {
VerticalScroller& verticalScroller;
Size minimumSize();
void setLength(unsigned length);
void setPosition(unsigned position);
pVerticalScroller(VerticalScroller& verticalScroller) : pWidget(verticalScroller), verticalScroller(verticalScroller) {}
void constructor();
void destructor();
void orphan();
void onChange(WPARAM wparam);
};
struct pVerticalSlider : public pWidget {
VerticalSlider& verticalSlider;
Size minimumSize();
void setLength(unsigned length);
void setPosition(unsigned position);
pVerticalSlider(VerticalSlider& verticalSlider) : pWidget(verticalSlider), verticalSlider(verticalSlider) {}
void constructor();
void destructor();
void orphan();
void onChange();
};
struct pViewport : public pWidget {
Viewport& viewport;
uintptr_t handle();
void setDroppable(bool droppable);
pViewport(Viewport& viewport) : pWidget(viewport), viewport(viewport) {}
void constructor();
void destructor();
void orphan();
};
using WindowProc = auto CALLBACK (*)(HWND, UINT, WPARAM, LPARAM) -> LRESULT;
}
#define Declare(Name, Base) \
p##Name(m##Name& reference) : p##Base(reference) {} \
auto self() const -> m##Name& { return (m##Name&)reference; } \
auto state() const -> m##Name::State& { return self().state; } \
auto construct() -> void override; \
auto destruct() -> void override; \
auto reconstruct() -> void override { destruct(), construct(); } \
#include "font.hpp"
#include "desktop.hpp"
#include "monitor.hpp"
#include "keyboard.hpp"
#include "mouse.hpp"
#include "browser-window.hpp"
#include "message-window.hpp"
#include "object.hpp"
#include "group.hpp"
#include "hotkey.hpp"
#include "timer.hpp"
#include "window.hpp"
#include "status-bar.hpp"
#include "menu-bar.hpp"
#include "popup-menu.hpp"
#include "action/action.hpp"
#include "action/menu.hpp"
#include "action/menu-separator.hpp"
#include "action/menu-item.hpp"
#include "action/menu-check-item.hpp"
#include "action/menu-radio-item.hpp"
#include "sizable.hpp"
#include "layout.hpp"
#include "widget/widget.hpp"
#include "widget/button.hpp"
#include "widget/canvas.hpp"
#include "widget/check-button.hpp"
#include "widget/check-label.hpp"
#include "widget/combo-button.hpp"
#include "widget/combo-button-item.hpp"
#include "widget/frame.hpp"
#include "widget/hex-edit.hpp"
#include "widget/horizontal-scroller.hpp"
#include "widget/horizontal-slider.hpp"
#include "widget/label.hpp"
#include "widget/line-edit.hpp"
#include "widget/list-view.hpp"
#include "widget/list-view-column.hpp"
#include "widget/list-view-item.hpp"
#include "widget/list-view-cell.hpp"
#include "widget/progress-bar.hpp"
#include "widget/radio-button.hpp"
#include "widget/radio-label.hpp"
#include "widget/tab-frame.hpp"
#include "widget/tab-frame-item.hpp"
#include "widget/text-edit.hpp"
#include "widget/vertical-scroller.hpp"
#include "widget/vertical-slider.hpp"
#include "widget/viewport.hpp"
#include "application.hpp"
#undef Declare

109
hiro/windows/popup-menu.cpp Normal file
View File

@@ -0,0 +1,109 @@
#if defined(Hiro_PopupMenu)
namespace hiro {
auto pPopupMenu::construct() -> void {
hwnd = CreateWindow(L"hiroPopupMenu", L"", ResizableStyle, 0, 0, 0, 0, 0, 0, GetModuleHandle(0), 0);
}
auto pPopupMenu::destruct() -> void {
if(hmenu) { DestroyMenu(hmenu); hmenu = nullptr; }
DestroyWindow(hwnd);
}
auto pPopupMenu::append(sAction action) -> void {
}
auto pPopupMenu::remove(sAction action) -> void {
}
auto pPopupMenu::setFont(const string& font) -> void {
}
auto pPopupMenu::setVisible(bool visible) -> void {
if(!visible) return;
if(hmenu) DestroyMenu(hmenu);
hmenu = CreatePopupMenu();
MENUINFO mi{sizeof(MENUINFO)};
mi.fMask = MIM_STYLE;
mi.dwStyle = MNS_NOTIFYBYPOS; //| MNS_MODELESS;
SetMenuInfo(hmenu, &mi);
unsigned position = 0;
for(auto& action : state().actions) {
if(!action->self()) continue;
action->self()->position = position;
unsigned enabled = action->enabled() ? 0 : MF_GRAYED;
MENUITEMINFO mii{sizeof(MENUITEMINFO)};
mii.fMask = MIIM_DATA;
mii.dwItemData = (ULONG_PTR)action.data();
if(auto menu = dynamic_cast<mMenu*>(action.data())) {
if(menu->visible()) {
menu->self()->_update();
AppendMenu(hmenu, MF_STRING | MF_POPUP | enabled, (UINT_PTR)menu->self()->hmenu, utf16_t(menu->text()));
if(auto bitmap = menu->self()->hbitmap) {
//Windows XP and below displays MIIM_BITMAP + hbmpItem in its own column (separate from check/radio marks)
//this causes too much spacing, so use a custom checkmark image instead
mii.fMask |= MIIM_CHECKMARKS;
mii.hbmpUnchecked = bitmap;
}
SetMenuItemInfo(hmenu, position++, true, &mii);
}
}
#if defined(Hiro_MenuSeparator)
else if(auto menuSeparator = dynamic_cast<mMenuSeparator*>(action.data())) {
if(menuSeparator->visible()) {
AppendMenu(hmenu, MF_SEPARATOR | enabled, position, L"");
SetMenuItemInfo(hmenu, position++, true, &mii);
}
}
#endif
#if defined(Hiro_MenuItem)
else if(auto menuItem = dynamic_cast<mMenuItem*>(action.data())) {
if(menuItem->visible()) {
AppendMenu(hmenu, MF_STRING | enabled, position, utf16_t(menuItem->text()));
if(auto bitmap = menuItem->self()->hbitmap) {
mii.fMask |= MIIM_CHECKMARKS;
mii.hbmpUnchecked = bitmap;
}
SetMenuItemInfo(hmenu, position++, true, &mii);
}
}
#endif
#if defined(Hiro_MenuCheckItem)
else if(auto menuCheckItem = dynamic_cast<mMenuCheckItem*>(action.data())) {
if(menuCheckItem->visible()) {
AppendMenu(hmenu, MF_STRING | enabled, position, utf16_t(menuCheckItem->text()));
SetMenuItemInfo(hmenu, position++, true, &mii);
if(menuCheckItem->checked()) menuCheckItem->setChecked();
}
}
#endif
#if defined(Hiro_MenuRadioItem)
else if(auto menuRadioItem = dynamic_cast<mMenuRadioItem*>(action.data())) {
if(menuRadioItem->visible()) {
AppendMenu(hmenu, MF_STRING | enabled, position, utf16_t(menuRadioItem->text()));
SetMenuItemInfo(hmenu, position++, true, &mii);
if(menuRadioItem->checked()) menuRadioItem->setChecked();
}
}
#endif
}
POINT point{0};
GetCursorPos(&point);
TrackPopupMenu(hmenu, TPM_LEFTALIGN | TPM_TOPALIGN, point.x, point.y, 0, hwnd, nullptr);
}
}
#endif

View File

@@ -0,0 +1,19 @@
#if defined(Hiro_PopupMenu)
namespace hiro {
struct pPopupMenu : pObject {
Declare(PopupMenu, Object)
auto append(sAction action) -> void;
auto remove(sAction action) -> void;
auto setFont(const string& font) -> void override;
auto setVisible(bool visible) -> void override;
HWND hwnd = nullptr;
HMENU hmenu = nullptr;
};
}
#endif

View File

@@ -1,5 +0,0 @@
namespace phoenix {
static Settings* settings = nullptr;
}

19
hiro/windows/sizable.cpp Normal file
View File

@@ -0,0 +1,19 @@
#if defined(Hiro_Sizable)
namespace hiro {
auto pSizable::construct() -> void {
}
auto pSizable::destruct() -> void {
}
auto pSizable::minimumSize() const -> Size {
}
auto pSizable::setGeometry(Geometry geometry) -> void {
}
}
#endif

14
hiro/windows/sizable.hpp Normal file
View File

@@ -0,0 +1,14 @@
#if defined(Hiro_Sizable)
namespace hiro {
struct pSizable : pObject {
Declare(Sizable, Object)
virtual auto minimumSize() const -> Size;
virtual auto setGeometry(Geometry geometry) -> void;
};
}
#endif

View File

@@ -0,0 +1,54 @@
#if defined(Hiro_StatusBar)
namespace hiro {
auto pStatusBar::construct() -> void {
if(auto parent = _parent()) {
hwnd = CreateWindow(STATUSCLASSNAME, L"", WS_CHILD | WS_DISABLED, 0, 0, 0, 0, parent->hwnd, nullptr, GetModuleHandle(0), 0);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
setEnabled(self().enabled(true));
setFont(self().font(true));
setText(self().text());
setVisible(self().visible(true));
}
}
auto pStatusBar::destruct() -> void {
if(hfont) { DeleteObject(hfont); hfont = nullptr; }
if(hwnd) { DestroyWindow(hwnd); hwnd = nullptr; }
if(auto parent = _parent()) {
parent->setGeometry(parent->state().geometry);
}
}
auto pStatusBar::setEnabled(bool enabled) -> void {
//unsupported
}
auto pStatusBar::setFont(const string& font) -> void {
if(hfont) DeleteObject(hfont);
hfont = pFont::create(font);
if(hwnd) SendMessage(hwnd, WM_SETFONT, (WPARAM)hfont, 0);
}
auto pStatusBar::setText(const string& text) -> void {
if(hwnd) SendMessage(hwnd, SB_SETTEXT, 0, (LPARAM)(wchar_t*)utf16_t(text));
}
auto pStatusBar::setVisible(bool visible) -> void {
if(hwnd) ShowWindow(hwnd, visible ? SW_SHOWNORMAL : SW_HIDE);
if(auto parent = _parent()) {
parent->setGeometry(parent->state().geometry);
}
}
auto pStatusBar::_parent() -> maybe<pWindow&> {
if(auto parent = self().parentWindow(true)) {
if(auto self = parent->self()) return *self;
}
return nothing;
}
}
#endif

View File

@@ -0,0 +1,21 @@
#if defined(Hiro_StatusBar)
namespace hiro {
struct pStatusBar : pObject {
Declare(StatusBar, Object)
auto setEnabled(bool enabled) -> void override;
auto setFont(const string& font) -> void override;
auto setText(const string& text) -> void;
auto setVisible(bool visible) -> void override;
auto _parent() -> maybe<pWindow&>;
HWND hwnd = 0;
HFONT hfont = 0;
};
}
#endif

View File

@@ -1,35 +1,39 @@
namespace phoenix {
#if defined(Hiro_Timer)
namespace hiro {
static vector<pTimer*> timers;
static void CALLBACK Timer_timeoutProc(HWND hwnd, UINT msg, UINT_PTR timerID, DWORD time) {
static auto CALLBACK Timer_timeoutProc(HWND hwnd, UINT msg, UINT_PTR timerID, DWORD time) -> void {
for(auto& timer : timers) {
if(timer->htimer == timerID) {
if(timer->timer.onActivate) timer->timer.onActivate();
return;
}
if(timer->htimer == timerID) return timer->self().doActivate();
}
}
void pTimer::setEnabled(bool enabled) {
auto pTimer::construct() -> void {
timers.append(this);
htimer = 0;
}
auto pTimer::destruct() -> void {
}
auto pTimer::setEnabled(bool enabled) -> void {
if(htimer) {
KillTimer(NULL, htimer);
htimer = 0;
}
if(enabled == true) {
htimer = SetTimer(NULL, 0u, timer.state.interval, Timer_timeoutProc);
htimer = SetTimer(NULL, 0u, state().interval, Timer_timeoutProc);
}
}
void pTimer::setInterval(unsigned interval) {
auto pTimer::setInterval(unsigned interval) -> void {
//destroy and recreate timer if interval changed
setEnabled(timer.state.enabled);
}
void pTimer::constructor() {
timers.append(this);
htimer = 0;
setEnabled(self().enabled(true));
}
}
#endif

16
hiro/windows/timer.hpp Normal file
View File

@@ -0,0 +1,16 @@
#if defined(Hiro_Timer)
namespace hiro {
struct pTimer : pObject {
Declare(Timer, Object)
auto setEnabled(bool enabled) -> void override;
auto setInterval(unsigned interval) -> void;
UINT_PTR htimer;
};
}
#endif

View File

@@ -1,18 +1,18 @@
namespace phoenix {
namespace hiro {
static const unsigned Windows2000 = 0x0500;
static const unsigned WindowsXP = 0x0501;
static const unsigned WindowsVista = 0x0600;
static const unsigned Windows7 = 0x0601;
static unsigned OsVersion() {
static auto OsVersion() -> unsigned {
OSVERSIONINFO versionInfo = {0};
versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&versionInfo);
return (versionInfo.dwMajorVersion << 8) + (versionInfo.dwMajorVersion << 0);
}
static HBITMAP CreateBitmap(const image& image) {
static auto CreateBitmap(const image& image) -> HBITMAP {
HDC hdc = GetDC(0);
BITMAPINFO bitmapInfo;
memset(&bitmapInfo, 0, sizeof(BITMAPINFO));
@@ -30,12 +30,16 @@ static HBITMAP CreateBitmap(const image& image) {
return hbitmap;
}
static lstring DropPaths(WPARAM wparam) {
static auto CreateRGB(const Color& color) -> COLORREF {
return RGB(color.red(), color.green(), color.blue());
}
static auto DropPaths(WPARAM wparam) -> lstring {
auto dropList = HDROP(wparam);
auto fileCount = DragQueryFile(dropList, ~0u, nullptr, 0);
lstring paths;
for(unsigned n = 0; n < fileCount; n++) {
for(auto n : range(fileCount)) {
auto length = DragQueryFile(dropList, n, nullptr, 0);
auto buffer = new wchar_t[length + 1];
@@ -52,29 +56,13 @@ static lstring DropPaths(WPARAM wparam) {
return paths;
}
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 unsigned GetWindowZOrder(HWND hwnd) {
static auto GetWindowZOrder(HWND hwnd) -> unsigned {
unsigned z = 0;
for(HWND next = hwnd; next != NULL; next = GetWindow(next, GW_HWNDPREV)) z++;
return z;
}
static void ImageList_Append(HIMAGELIST imageList, const nall::image& source, unsigned scale) {
static auto ImageList_Append(HIMAGELIST imageList, const image& source, unsigned scale) -> void {
auto image = source;
if(image.empty()) {
image.allocate(scale, scale);
@@ -87,128 +75,15 @@ static void ImageList_Append(HIMAGELIST imageList, const nall::image& source, un
DeleteObject(bitmap);
}
static Keyboard::Keycode Keysym(unsigned keysym, unsigned keyflags) {
#define pressed(keysym) (GetAsyncKeyState(keysym) & 0x8000)
#define enabled(keysym) (GetKeyState(keysym))
#define shifted() (pressed(VK_LSHIFT) || pressed(VK_RSHIFT))
#define extended() (keyflags & (1 << 24))
switch(keysym) {
case VK_ESCAPE: return Keyboard::Keycode::Escape;
case VK_F1: return Keyboard::Keycode::F1;
case VK_F2: return Keyboard::Keycode::F2;
case VK_F3: return Keyboard::Keycode::F3;
case VK_F4: return Keyboard::Keycode::F4;
case VK_F5: return Keyboard::Keycode::F5;
case VK_F6: return Keyboard::Keycode::F6;
case VK_F7: return Keyboard::Keycode::F7;
case VK_F8: return Keyboard::Keycode::F8;
case VK_F9: return Keyboard::Keycode::F9;
//Keyboard::Keycode::F10 (should be captured under VK_MENU from WM_SYSKEY(UP,DOWN); but this is not working...)
case VK_F11: return Keyboard::Keycode::F11;
case VK_F12: return Keyboard::Keycode::F12;
//Keyboard::Keycode::PrintScreen
//Keyboard::Keycode::SysRq
case VK_SCROLL: return Keyboard::Keycode::ScrollLock;
case VK_PAUSE: return Keyboard::Keycode::Pause;
//Keyboard::Keycode::Break
case VK_INSERT: return extended() ? Keyboard::Keycode::Insert : Keyboard::Keycode::KeypadInsert;
case VK_DELETE: return extended() ? Keyboard::Keycode::Delete : Keyboard::Keycode::KeypadDelete;
case VK_HOME: return extended() ? Keyboard::Keycode::Home : Keyboard::Keycode::KeypadHome;
case VK_END: return extended() ? Keyboard::Keycode::End : Keyboard::Keycode::KeypadEnd;
case VK_PRIOR: return extended() ? Keyboard::Keycode::PageUp : Keyboard::Keycode::KeypadPageUp;
case VK_NEXT: return extended() ? Keyboard::Keycode::PageDown : Keyboard::Keycode::KeypadPageDown;
case VK_UP: return extended() ? Keyboard::Keycode::Up : Keyboard::Keycode::KeypadUp;
case VK_DOWN: return extended() ? Keyboard::Keycode::Down : Keyboard::Keycode::KeypadDown;
case VK_LEFT: return extended() ? Keyboard::Keycode::Left : Keyboard::Keycode::KeypadLeft;
case VK_RIGHT: return extended() ? Keyboard::Keycode::Right : Keyboard::Keycode::KeypadRight;
case VK_OEM_3: return !shifted() ? Keyboard::Keycode::Grave : Keyboard::Keycode::Tilde;
case '1': return !shifted() ? Keyboard::Keycode::Number1 : Keyboard::Keycode::Exclamation;
case '2': return !shifted() ? Keyboard::Keycode::Number2 : Keyboard::Keycode::At;
case '3': return !shifted() ? Keyboard::Keycode::Number3 : Keyboard::Keycode::Pound;
case '4': return !shifted() ? Keyboard::Keycode::Number4 : Keyboard::Keycode::Dollar;
case '5': return !shifted() ? Keyboard::Keycode::Number5 : Keyboard::Keycode::Percent;
case '6': return !shifted() ? Keyboard::Keycode::Number6 : Keyboard::Keycode::Power;
case '7': return !shifted() ? Keyboard::Keycode::Number7 : Keyboard::Keycode::Ampersand;
case '8': return !shifted() ? Keyboard::Keycode::Number8 : Keyboard::Keycode::Asterisk;
case '9': return !shifted() ? Keyboard::Keycode::Number9 : Keyboard::Keycode::ParenthesisLeft;
case '0': return !shifted() ? Keyboard::Keycode::Number0 : Keyboard::Keycode::ParenthesisRight;
case VK_OEM_MINUS: return !shifted() ? Keyboard::Keycode::Minus : Keyboard::Keycode::Underscore;
case VK_OEM_PLUS: return !shifted() ? Keyboard::Keycode::Equal : Keyboard::Keycode::Plus;
case VK_BACK: return Keyboard::Keycode::Backspace;
case VK_OEM_4: return !shifted() ? Keyboard::Keycode::BracketLeft : Keyboard::Keycode::BraceLeft;
case VK_OEM_6: return !shifted() ? Keyboard::Keycode::BracketRight : Keyboard::Keycode::BraceRight;
case VK_OEM_5: return !shifted() ? Keyboard::Keycode::Backslash : Keyboard::Keycode::Pipe;
case VK_OEM_1: return !shifted() ? Keyboard::Keycode::Semicolon : Keyboard::Keycode::Colon;
case VK_OEM_7: return !shifted() ? Keyboard::Keycode::Apostrophe : Keyboard::Keycode::Quote;
case VK_OEM_COMMA: return !shifted() ? Keyboard::Keycode::Comma : Keyboard::Keycode::CaretLeft;
case VK_OEM_PERIOD: return !shifted() ? Keyboard::Keycode::Period : Keyboard::Keycode::CaretRight;
case VK_OEM_2: return !shifted() ? Keyboard::Keycode::Slash : Keyboard::Keycode::Question;
case VK_TAB: return Keyboard::Keycode::Tab;
case VK_CAPITAL: return Keyboard::Keycode::CapsLock;
case VK_RETURN: return !extended() ? Keyboard::Keycode::Return : Keyboard::Keycode::Enter;
case VK_SHIFT: return !pressed(VK_RSHIFT) ? Keyboard::Keycode::ShiftLeft : Keyboard::Keycode::ShiftRight;
case VK_CONTROL: return !pressed(VK_RCONTROL) ? Keyboard::Keycode::ControlLeft : Keyboard::Keycode::ControlRight;
case VK_LWIN: return Keyboard::Keycode::SuperLeft;
case VK_RWIN: return Keyboard::Keycode::SuperRight;
case VK_MENU:
if(keyflags & (1 << 24)) return Keyboard::Keycode::AltRight;
return Keyboard::Keycode::AltLeft;
case VK_SPACE: return Keyboard::Keycode::Space;
case VK_APPS: return Keyboard::Keycode::Menu;
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M':
case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z':
if(enabled(VK_CAPITAL)) {
if(shifted()) {
return (Keyboard::Keycode)((unsigned)Keyboard::Keycode::a + keysym - 'A');
} else {
return (Keyboard::Keycode)((unsigned)Keyboard::Keycode::A + keysym - 'A');
}
} else {
if(shifted()) {
return (Keyboard::Keycode)((unsigned)Keyboard::Keycode::A + keysym - 'A');
} else {
return (Keyboard::Keycode)((unsigned)Keyboard::Keycode::a + keysym - 'A');
}
}
break;
case VK_NUMLOCK: return Keyboard::Keycode::NumLock;
case VK_DIVIDE: return Keyboard::Keycode::Divide;
case VK_MULTIPLY: return Keyboard::Keycode::Multiply;
case VK_SUBTRACT: return Keyboard::Keycode::Subtract;
case VK_ADD: return Keyboard::Keycode::Add;
case VK_DECIMAL: return Keyboard::Keycode::Point;
case VK_NUMPAD1: return Keyboard::Keycode::Keypad1;
case VK_NUMPAD2: return Keyboard::Keycode::Keypad2;
case VK_NUMPAD3: return Keyboard::Keycode::Keypad3;
case VK_NUMPAD4: return Keyboard::Keycode::Keypad4;
case VK_NUMPAD5: return Keyboard::Keycode::Keypad5;
case VK_NUMPAD6: return Keyboard::Keycode::Keypad6;
case VK_NUMPAD7: return Keyboard::Keycode::Keypad7;
case VK_NUMPAD8: return Keyboard::Keycode::Keypad8;
case VK_NUMPAD9: return Keyboard::Keycode::Keypad9;
case VK_NUMPAD0: return Keyboard::Keycode::Keypad0;
case VK_CLEAR: return Keyboard::Keycode::KeypadCenter;
//post message only if said message is not already pending in the queue
static auto PostMessageOnce(HWND hwnd, UINT id, WPARAM wparam, LPARAM lparam) -> void {
MSG msg;
if(!PeekMessage(&msg, hwnd, id, id, PM_NOREMOVE)) {
PostMessage(hwnd, id, wparam, lparam);
}
return Keyboard::Keycode::None;
#undef pressed
#undef enabled
#undef shifted
#undef extended
}
static unsigned ScrollEvent(HWND hwnd, WPARAM wparam) {
static auto ScrollEvent(HWND hwnd, WPARAM wparam) -> unsigned {
SCROLLINFO info;
memset(&info, 0, sizeof(SCROLLINFO));
info.cbSize = sizeof(SCROLLINFO);
@@ -233,121 +108,261 @@ static unsigned ScrollEvent(HWND hwnd, WPARAM wparam) {
return info.nPos;
}
static LRESULT CALLBACK Shared_windowProc(WindowProc windowProc, HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
Object* object = (Object*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if(object == nullptr) return DefWindowProc(hwnd, msg, wparam, lparam);
Window& window = dynamic_cast<Window*>(object) ? *(Window*)object : *((Widget*)object)->Sizable::state.window;
//separate because PopupMenu HWND does not contain GWLP_USERDATA pointing at Window needed for Shared_windowProc
static auto CALLBACK Menu_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> LRESULT {
switch(msg) {
case WM_MENUCOMMAND: {
MENUITEMINFO mii{sizeof(MENUITEMINFO)};
mii.fMask = MIIM_DATA;
GetMenuItemInfo((HMENU)lparam, wparam, true, &mii);
auto object = (mObject*)mii.dwItemData;
if(!object) break;
#if defined(Hiro_MenuItem)
if(auto menuItem = dynamic_cast<mMenuItem*>(object)) {
return menuItem->self()->onActivate(), false;
}
#endif
#if defined(Hiro_MenuCheckItem)
if(auto menuCheckItem = dynamic_cast<mMenuCheckItem*>(object)) {
return menuCheckItem->self()->onToggle(), false;
}
#endif
#if defined(Hiro_MenuRadioItem)
if(auto menuRadioItem = dynamic_cast<mMenuRadioItem*>(object)) {
return menuRadioItem->self()->onActivate(), false;
}
#endif
break;
}
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
static auto CALLBACK Shared_windowProc(WindowProc windowProc, HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> LRESULT {
auto object = (mObject*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if(!object) return DefWindowProc(hwnd, msg, wparam, lparam);
auto window = dynamic_cast<mWindow*>(object);
if(!window) window = object->parentWindow(true);
if(!window) return DefWindowProc(hwnd, msg, wparam, lparam);
bool process = true;
if(!pWindow::modal.empty() && !pWindow::modal.find(&window.p)) process = false;
if(applicationState.quit) process = false;
if(process == false) return DefWindowProc(hwnd, msg, wparam, lparam);
if(pWindow::modal && !pWindow::modal.find(window->self())) process = false;
if(Application::state.quit) process = false;
if(!process) return DefWindowProc(hwnd, msg, wparam, lparam);
switch(msg) {
case WM_CTLCOLORBTN:
case WM_CTLCOLOREDIT:
case WM_CTLCOLORSTATIC: {
Object* object = (Object*)GetWindowLongPtr((HWND)lparam, GWLP_USERDATA);
if(object == nullptr) break;
auto object = (mObject*)GetWindowLongPtr((HWND)lparam, GWLP_USERDATA);
if(!object) break;
//allow custom colors for various widgets
//note that this happens always: default colors are black text on a white background, unless overridden
//this intentionally overrides the default behavior of Windows to paint disabled controls with the window background color
if(dynamic_cast<Console*>(object)) {
Console& console = *(Console*)object;
Color& background = console.state.backgroundColor;
Color& foreground = console.state.foregroundColor;
SetTextColor((HDC)wparam, RGB(foreground.red, foreground.green, foreground.blue));
SetBkColor((HDC)wparam, RGB(background.red, background.green, background.blue));
return (LRESULT)console.p.backgroundBrush;
} else if(dynamic_cast<HexEdit*>(object)) {
HexEdit& hexEdit = *(HexEdit*)object;
Color& background = hexEdit.state.backgroundColor;
Color& foreground = hexEdit.state.foregroundColor;
SetTextColor((HDC)wparam, RGB(foreground.red, foreground.green, foreground.blue));
SetBkColor((HDC)wparam, RGB(background.red, background.green, background.blue));
return (LRESULT)hexEdit.p.backgroundBrush;
} else if(dynamic_cast<LineEdit*>(object)) {
LineEdit& lineEdit = *(LineEdit*)object;
Color& background = lineEdit.state.backgroundColor;
Color& foreground = lineEdit.state.foregroundColor;
SetTextColor((HDC)wparam, RGB(foreground.red, foreground.green, foreground.blue));
SetBkColor((HDC)wparam, RGB(background.red, background.green, background.blue));
return (LRESULT)lineEdit.p.backgroundBrush;
} else if(dynamic_cast<TextEdit*>(object)) {
TextEdit& textEdit = *(TextEdit*)object;
Color& background = textEdit.state.backgroundColor;
Color& foreground = textEdit.state.foregroundColor;
SetTextColor((HDC)wparam, RGB(foreground.red, foreground.green, foreground.blue));
SetBkColor((HDC)wparam, RGB(background.red, background.green, background.blue));
return (LRESULT)textEdit.p.backgroundBrush;
} else if(!GetParentWidget((Sizable*)object) && window.p.brush) {
SetBkColor((HDC)wparam, window.p.brushColor);
return (INT_PTR)window.p.brush;
#if defined(Hiro_Window) && defined(Hiro_TabFrame)
if(!object->parentTabFrame(true) && window->self()->hbrush) {
SetBkColor((HDC)wparam, window->self()->hbrushColor);
return (LRESULT)window->self()->hbrush;
}
#endif
#if defined(Hiro_HexEdit)
if(auto hexEdit = dynamic_cast<mHexEdit*>(object)) {
if(auto background = hexEdit->backgroundColor()) SetBkColor((HDC)wparam, CreateRGB(background));
if(auto foreground = hexEdit->foregroundColor()) SetTextColor((HDC)wparam, CreateRGB(foreground));
return (LRESULT)hexEdit->self()->backgroundBrush;
}
#endif
#if defined(Hiro_LineEdit)
if(auto lineEdit = dynamic_cast<mLineEdit*>(object)) {
if(auto background = lineEdit->backgroundColor()) SetBkColor((HDC)wparam, CreateRGB(background));
if(auto foreground = lineEdit->foregroundColor()) SetTextColor((HDC)wparam, CreateRGB(foreground));
return (LRESULT)lineEdit->self()->backgroundBrush;
}
#endif
#if defined(Hiro_TextEdit)
if(auto textEdit = dynamic_cast<mTextEdit*>(object)) {
if(auto background = textEdit->backgroundColor()) SetBkColor((HDC)wparam, CreateRGB(background));
if(auto foreground = textEdit->foregroundColor()) SetTextColor((HDC)wparam, CreateRGB(foreground));
return (LRESULT)textEdit->self()->backgroundBrush;
}
#endif
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); return TRUE; }
auto drawItem = (LPDRAWITEMSTRUCT)lparam;
auto object = (mObject*)GetWindowLongPtr((HWND)drawItem->hwndItem, GWLP_USERDATA);
if(!object) break;
#if defined(Hiro_TabFrame)
if(auto tabFrame = dynamic_cast<mTabFrame*>(object)) {
return tabFrame->self()->onDrawItem(lparam), true;
}
#endif
break;
}
case WM_MENUCOMMAND: {
return Menu_windowProc(hwnd, msg, wparam, lparam);
}
case WM_COMMAND: {
unsigned id = LOWORD(wparam);
HWND control = GetDlgItem(hwnd, id);
Object* object = control ? (Object*)GetWindowLongPtr(control, GWLP_USERDATA) : pObject::find(id);
if(object == nullptr) 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<RadioItem*>(object)) { ((RadioItem*)object)->p.onActivate(); 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; }
if(!lparam) break;
auto object = (mObject*)GetWindowLongPtr((HWND)lparam, GWLP_USERDATA);
if(!object) break;
#if defined(Hiro_Button)
if(auto button = dynamic_cast<mButton*>(object)) {
return button->self()->onActivate(), false;
}
#endif
#if defined(Hiro_CheckButton)
if(auto checkButton = dynamic_cast<mCheckButton*>(object)) {
return checkButton->self()->onToggle(), false;
}
#endif
#if defined(Hiro_CheckLabel)
if(auto checkLabel = dynamic_cast<mCheckLabel*>(object)) {
return checkLabel->self()->onToggle(), false;
}
#endif
#if defined(Hiro_ComboButton)
if(auto comboButton = dynamic_cast<mComboButton*>(object)) {
if(HIWORD(wparam) == CBN_SELCHANGE) {
return comboButton->self()->onChange(), false;
}
}
#endif
#if defined(Hiro_LineEdit)
if(auto lineEdit = dynamic_cast<mLineEdit*>(object)) {
if(HIWORD(wparam) == EN_CHANGE) {
return lineEdit->self()->onChange(), false;
}
}
#endif
#if defined(Hiro_RadioButton)
if(auto radioButton = dynamic_cast<mRadioButton*>(object)) {
return radioButton->self()->onActivate(), false;
}
#endif
#if defined(Hiro_RadioLabel)
if(auto radioLabel = dynamic_cast<mRadioLabel*>(object)) {
return radioLabel->self()->onActivate(), false;
}
#endif
#if defined(Hiro_TextEdit)
if(auto textEdit = dynamic_cast<mTextEdit*>(object)) {
if(HIWORD(wparam) == EN_CHANGE) {
return textEdit->self()->onChange(), false;
}
}
#endif
break;
}
case WM_NOTIFY: {
unsigned id = LOWORD(wparam);
HWND control = GetDlgItem(hwnd, id);
Object* object = (Object*)GetWindowLongPtr(control, GWLP_USERDATA);
if(object == nullptr) break;
if(dynamic_cast<ListView*>(object) && ((LPNMHDR)lparam)->code == LVN_ITEMACTIVATE) { ((ListView*)object)->p.onActivate(lparam); break; }
if(dynamic_cast<ListView*>(object) && ((LPNMHDR)lparam)->code == LVN_ITEMCHANGED) { ((ListView*)object)->p.onChange(lparam); break; }
if(dynamic_cast<ListView*>(object) && ((LPNMHDR)lparam)->code == NM_CUSTOMDRAW) { return ((ListView*)object)->p.onCustomDraw(lparam); }
if(dynamic_cast<TabFrame*>(object) && ((LPNMHDR)lparam)->code == TCN_SELCHANGE) { ((TabFrame*)object)->p.onChange(); break; }
auto header = (LPNMHDR)lparam;
auto object = (mObject*)GetWindowLongPtr((HWND)header->hwndFrom, GWLP_USERDATA);
if(!object) break;
#if defined(Hiro_ListView)
if(auto listView = dynamic_cast<mListView*>(object)) {
if(header->code == LVN_ITEMACTIVATE) {
listView->self()->onActivate(lparam);
break;
}
if(header->code == LVN_ITEMCHANGED) {
listView->self()->onChange(lparam);
break;
}
if(header->code == LVN_COLUMNCLICK) {
listView->self()->onSort(lparam);
break;
}
if(header->code == NM_RCLICK) {
listView->self()->onContext(lparam);
break;
}
if(header->code == NM_CUSTOMDRAW) {
return listView->self()->onCustomDraw(lparam);
}
}
#endif
#if defined(Hiro_TabFrame)
if(auto tabFrame = dynamic_cast<mTabFrame*>(object)) {
if(header->code == TCN_SELCHANGE) {
tabFrame->self()->onChange();
break;
}
}
#endif
break;
}
case WM_APP + AppMessage::ListView_onActivate: {
ListView* listView = (ListView*)lparam;
if(listView && listView->onActivate) listView->onActivate();
#if defined(Hiro_ListView)
case AppMessage::ListView_onActivate: {
if(auto listView = (mListView*)lparam) listView->doActivate();
break;
}
case AppMessage::ListView_onChange: {
if(auto listView = (mListView*)lparam) listView->doChange();
}
#endif
case WM_HSCROLL:
case WM_VSCROLL: {
Object* object = nullptr;
if(lparam) {
object = (Object*)GetWindowLongPtr((HWND)lparam, GWLP_USERDATA);
} else {
unsigned id = LOWORD(wparam);
HWND control = GetDlgItem(hwnd, id);
object = (Object*)GetWindowLongPtr(control, GWLP_USERDATA);
if(!lparam) break;
auto object = (mObject*)GetWindowLongPtr((HWND)lparam, GWLP_USERDATA);
if(!object) break;
#if defined(Hiro_HorizontalScroller)
if(auto horizontalScroller = dynamic_cast<mHorizontalScroller*>(object)) {
return horizontalScroller->self()->onChange(wparam), true;
}
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(); return TRUE; }
if(dynamic_cast<VerticalSlider*>(object)) { ((VerticalSlider*)object)->p.onChange(); return TRUE; }
#endif
#if defined(Hiro_HorizontalSlider)
if(auto horizontalSlider = dynamic_cast<mHorizontalSlider*>(object)) {
return horizontalSlider->self()->onChange(), true;
}
#endif
#if defined(Hiro_VerticalScroller)
if(auto verticalScroller = dynamic_cast<mVerticalScroller*>(object)) {
return verticalScroller->self()->onChange(wparam), true;
}
#endif
#if defined(Hiro_VerticalSlider)
if(auto verticalSlider = dynamic_cast<mVerticalSlider*>(object)) {
return verticalSlider->self()->onChange(), true;
}
#endif
break;
}
}

View File

@@ -1,102 +1,94 @@
namespace phoenix {
#if defined(Hiro_Button)
#ifndef Button_SetImageList
//MinGW/32-bit has painfully outdated platform headers ...
typedef struct {
HIMAGELIST himl;
RECT margin;
UINT uAlign;
} BUTTON_IMAGELIST, *PBUTTON_IMAGELIST;
namespace hiro {
#define BUTTON_IMAGELIST_ALIGN_LEFT 0
#define BUTTON_IMAGELIST_ALIGN_RIGHT 1
#define BUTTON_IMAGELIST_ALIGN_TOP 2
#define BUTTON_IMAGELIST_ALIGN_BOTTOM 3
#define BUTTON_IMAGELIST_ALIGN_CENTER 4
#define BCM_FIRST 0x1600
#define BCM_SETIMAGELIST (BCM_FIRST+2)
#define Button_SetImageList(hwnd, pbuttonImagelist) (WINBOOL)SNDMSG((hwnd),BCM_SETIMAGELIST,0,(LPARAM)(pbuttonImagelist))
#endif
Size pButton::minimumSize() {
Size size = pFont::size(hfont, button.state.text);
if(button.state.orientation == Orientation::Horizontal) {
size.width += button.state.image.width;
size.height = max(button.state.image.height, size.height);
}
if(button.state.orientation == Orientation::Vertical) {
size.width = max(button.state.image.width, size.width);
size.height += button.state.image.height;
}
return {size.width + (button.state.text ? 20 : 10), size.height + 10};
auto pButton::construct() -> void {
hwnd = CreateWindow(
L"BUTTON", L"",
WS_CHILD | WS_TABSTOP,
0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0
);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
pWidget::_setState();
_setState();
setBordered(state().bordered);
}
void pButton::setBordered(bool bordered) {
auto pButton::destruct() -> void {
if(hbitmap) { DeleteObject(hbitmap); hbitmap = 0; }
if(himagelist) { ImageList_Destroy(himagelist); himagelist = 0; }
DestroyWindow(hwnd);
}
void pButton::setImage(const image& image, Orientation orientation) {
nall::image nallImage = image;
nallImage.transform(0, 32, 255u << 24, 255u << 16, 255u << 8, 255u << 0);
auto pButton::minimumSize() const -> Size {
auto size = pFont::size(hfont, state().text);
if(state().orientation == Orientation::Horizontal) {
size.setWidth(size.width() + state().icon.width);
size.setHeight(max(size.height(), state().icon.height));
}
if(state().orientation == Orientation::Vertical) {
size.setWidth(max(size.width(), state().icon.width));
size.setHeight(size.height() + state().icon.height);
}
return {size.width() + (state().text ? 20 : 13), size.height() + 10};
}
auto pButton::setBordered(bool bordered) -> void {
}
auto pButton::setIcon(const image& icon) -> void {
_setState();
}
auto pButton::setOrientation(Orientation orientation) -> void {
_setState();
}
auto pButton::setText(const string& text) -> void {
_setState();
}
auto pButton::onActivate() -> void {
self().doActivate();
}
//performs setIcon, setOrientation, setText
auto pButton::_setState() -> void {
image icon = state().icon;
icon.transform(0, 32, 255u << 24, 255u << 16, 255u << 8, 255u << 0);
if(hbitmap) { DeleteObject(hbitmap); hbitmap = 0; }
if(himagelist) { ImageList_Destroy(himagelist); himagelist = 0; }
//Windows XP and earlier do not support translucent images
//so we must blend with the button background color (which does not look great with XP gradient-button themes)
if(OsVersion() < WindowsVista) nallImage.alphaBlend(GetSysColor(COLOR_BTNFACE));
hbitmap = CreateBitmap(nallImage);
himagelist = ImageList_Create(nallImage.width, nallImage.height, ILC_COLOR32, 1, 0);
if(OsVersion() < WindowsVista) icon.alphaBlend(GetSysColor(COLOR_BTNFACE));
hbitmap = CreateBitmap(icon);
himagelist = ImageList_Create(icon.width, icon.height, ILC_COLOR32, 1, 0);
ImageList_Add(himagelist, hbitmap, NULL);
BUTTON_IMAGELIST list;
list.himl = himagelist;
switch(orientation) {
switch(state().orientation) {
case Orientation::Horizontal: SetRect(&list.margin, 5, 0, 0, 0); list.uAlign = BUTTON_IMAGELIST_ALIGN_LEFT; break;
case Orientation::Vertical: SetRect(&list.margin, 0, 5, 0, 0); list.uAlign = BUTTON_IMAGELIST_ALIGN_TOP; break;
}
Button_SetImageList(hwnd, &list);
setText(button.state.text); //update text to display nicely with image (or lack thereof)
}
void pButton::setText(string text) {
if(text.empty()) {
if(auto text = state().text) {
//text will not show up if BS_BITMAP is set
SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) &~ BS_BITMAP);
SetWindowText(hwnd, utf16_t(text));
} else {
//bitmaps will not show up if text is empty
SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) | BS_BITMAP);
} else {
//text will not show up if BS_BITMAP is set
SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) & ~BS_BITMAP);
SetWindowText(hwnd, L"");
}
SetWindowText(hwnd, utf16_t(text));
}
void pButton::constructor() {
hwnd = CreateWindow(L"BUTTON", L"", WS_CHILD | WS_TABSTOP, 0, 0, 0, 0, parentHwnd, (HMENU)id, GetModuleHandle(0), 0);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&button);
setDefaultFont();
setBordered(button.state.bordered);
setImage(button.state.image, button.state.orientation);
//setText(button.state.text); //called by setImage();
synchronize();
}
void pButton::destructor() {
if(hbitmap) { DeleteObject(hbitmap); hbitmap = 0; }
if(himagelist) { ImageList_Destroy(himagelist); himagelist = 0; }
DestroyWindow(hwnd);
}
void pButton::orphan() {
destructor();
constructor();
}
void pButton::onActivate() {
if(button.onActivate) button.onActivate();
}
}
#endif

View File

@@ -0,0 +1,24 @@
#if defined(Hiro_Button)
namespace hiro {
struct pButton : pWidget {
Declare(Button, Widget)
auto minimumSize() const -> Size override;
auto setBordered(bool bordered) -> void;
auto setIcon(const image& icon) -> void;
auto setOrientation(Orientation orientation) -> void;
auto setText(const string& text) -> void;
auto onActivate() -> void;
auto _setState() -> void;
HBITMAP hbitmap;
HIMAGELIST himagelist;
};
}
#endif

View File

@@ -1,17 +1,16 @@
namespace phoenix {
#if defined(Hiro_Canvas)
static LRESULT CALLBACK Canvas_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
Object* object = (Object*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if(object == nullptr) return DefWindowProc(hwnd, msg, wparam, lparam);
if(!dynamic_cast<Canvas*>(object)) return DefWindowProc(hwnd, msg, wparam, lparam);
Canvas& canvas = (Canvas&)*object;
namespace hiro {
static auto CALLBACK Canvas_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> LRESULT {
auto object = (mObject*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if(!object) return DefWindowProc(hwnd, msg, wparam, lparam);
auto canvas = dynamic_cast<mCanvas*>(object);
if(!canvas) return DefWindowProc(hwnd, msg, wparam, lparam);
if(msg == WM_DROPFILES) {
lstring paths = DropPaths(wparam);
if(paths.empty() == false) {
if(canvas.onDrop) canvas.onDrop(paths);
}
return FALSE;
if(auto paths = DropPaths(wparam)) canvas->doDrop(paths);
return false;
}
if(msg == WM_GETDLGCODE) {
@@ -20,103 +19,97 @@ static LRESULT CALLBACK Canvas_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LP
if(msg == WM_ERASEBKGND) {
//background is erased during WM_PAINT to prevent flickering
return TRUE;
return true;
}
if(msg == WM_PAINT) {
canvas.p.paint();
return TRUE;
if(auto self = canvas->self()) self->_paint();
return true;
}
if(msg == WM_MOUSEMOVE) {
TRACKMOUSEEVENT tracker = {sizeof(TRACKMOUSEEVENT), TME_LEAVE, hwnd};
TRACKMOUSEEVENT tracker{sizeof(TRACKMOUSEEVENT), TME_LEAVE, hwnd};
TrackMouseEvent(&tracker);
if(canvas.onMouseMove) canvas.onMouseMove({(int16_t)LOWORD(lparam), (int16_t)HIWORD(lparam)});
canvas->doMouseMove({(int16_t)LOWORD(lparam), (int16_t)HIWORD(lparam)});
}
if(msg == WM_MOUSELEAVE) {
if(canvas.onMouseLeave) canvas.onMouseLeave();
canvas->doMouseLeave();
}
if(msg == WM_LBUTTONDOWN || msg == WM_MBUTTONDOWN || msg == WM_RBUTTONDOWN) {
if(canvas.onMousePress) switch(msg) {
case WM_LBUTTONDOWN: canvas.onMousePress(Mouse::Button::Left); break;
case WM_MBUTTONDOWN: canvas.onMousePress(Mouse::Button::Middle); break;
case WM_RBUTTONDOWN: canvas.onMousePress(Mouse::Button::Right); break;
switch(msg) {
case WM_LBUTTONDOWN: canvas->doMousePress(Mouse::Button::Left); break;
case WM_MBUTTONDOWN: canvas->doMousePress(Mouse::Button::Middle); break;
case WM_RBUTTONDOWN: canvas->doMousePress(Mouse::Button::Right); break;
}
}
if(msg == WM_LBUTTONUP || msg == WM_MBUTTONUP || msg == WM_RBUTTONUP) {
if(canvas.onMouseRelease) switch(msg) {
case WM_LBUTTONUP: canvas.onMouseRelease(Mouse::Button::Left); break;
case WM_MBUTTONUP: canvas.onMouseRelease(Mouse::Button::Middle); break;
case WM_RBUTTONUP: canvas.onMouseRelease(Mouse::Button::Right); break;
switch(msg) {
case WM_LBUTTONUP: canvas->doMouseRelease(Mouse::Button::Left); break;
case WM_MBUTTONUP: canvas->doMouseRelease(Mouse::Button::Middle); break;
case WM_RBUTTONUP: canvas->doMouseRelease(Mouse::Button::Right); break;
}
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
void pCanvas::setDroppable(bool droppable) {
DragAcceptFiles(hwnd, droppable);
auto pCanvas::construct() -> void {
hwnd = CreateWindow(L"hiroCanvas", L"", WS_CHILD, 0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
pWidget::_setState();
setDroppable(state().droppable);
update();
}
void pCanvas::setGeometry(Geometry geometry) {
if(canvas.state.width == 0 || canvas.state.height == 0) rasterize();
unsigned width = canvas.state.width;
unsigned height = canvas.state.height;
if(width == 0) width = widget.state.geometry.width;
if(height == 0) height = widget.state.geometry.height;
if(width < geometry.width) {
geometry.x += (geometry.width - width) / 2;
geometry.width = width;
}
if(height < geometry.height) {
geometry.y += (geometry.height - height) / 2;
geometry.height = height;
}
pWidget::setGeometry(geometry);
}
void pCanvas::setMode(Canvas::Mode mode) {
rasterize(), redraw();
}
void pCanvas::setSize(Size size) {
rasterize(), redraw();
}
void pCanvas::constructor() {
hwnd = CreateWindow(L"phoenix_canvas", L"", WS_CHILD, 0, 0, 0, 0, parentHwnd, (HMENU)id, GetModuleHandle(0), 0);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&canvas);
setDroppable(canvas.state.droppable);
rasterize();
synchronize();
}
void pCanvas::destructor() {
release();
auto pCanvas::destruct() -> void {
DestroyWindow(hwnd);
}
void pCanvas::orphan() {
destructor();
constructor();
auto pCanvas::minimumSize() const -> Size {
return {max(0, state().size.width()), max(0, state().size.height())};
}
void pCanvas::paint() {
if(surface == nullptr) return;
auto pCanvas::setColor(Color color) -> void {
mode = Mode::Color;
update();
}
auto pCanvas::setData(Size size) -> void {
mode = Mode::Data;
update();
}
auto pCanvas::setDroppable(bool droppable) -> void {
DragAcceptFiles(hwnd, droppable);
}
auto pCanvas::setGeometry(Geometry geometry) -> void {
pWidget::setGeometry(geometry);
update();
}
auto pCanvas::setGradient(Color topLeft, Color topRight, Color bottomLeft, Color bottomRight) -> void {
mode = Mode::Gradient;
update();
}
auto pCanvas::setIcon(const image& icon) -> void {
mode = Mode::Icon;
update();
}
auto pCanvas::update() -> void {
_rasterize();
_redraw();
}
auto pCanvas::_paint() -> void {
PAINTSTRUCT ps;
BeginPaint(hwnd, &ps);
uint32_t* data = surface;
unsigned width = surfaceWidth;
unsigned height = surfaceHeight;
HDC hdc = CreateCompatibleDC(ps.hdc);
BITMAPINFO bmi;
memset(&bmi, 0, sizeof(BITMAPINFO));
@@ -126,21 +119,17 @@ void pCanvas::paint() {
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biWidth = width;
bmi.bmiHeader.biHeight = -height; //GDI stores bitmaps upside now; negative height flips bitmap
bmi.bmiHeader.biSizeImage = width * height * sizeof(uint32_t);
bmi.bmiHeader.biSizeImage = pixels.size() * sizeof(uint32_t);
void* bits = nullptr;
HBITMAP bitmap = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, &bits, NULL, 0);
if(bits) memcpy(bits, data, width * height * sizeof(uint32_t));
if(bits) memory::copy(bits, pixels.data(), pixels.size() * sizeof(uint32_t));
SelectObject(hdc, bitmap);
BLENDFUNCTION bf;
bf.BlendOp = AC_SRC_OVER;
bf.BlendFlags = 0;
bf.SourceConstantAlpha = 255;
bf.AlphaFormat = AC_SRC_ALPHA;
RECT rc;
GetClientRect(hwnd, &rc);
DrawThemeParentBackground(hwnd, ps.hdc, &rc);
BLENDFUNCTION bf{AC_SRC_OVER, 0, (BYTE)255, AC_SRC_ALPHA};
AlphaBlend(ps.hdc, 0, 0, width, height, hdc, 0, 0, width, height, bf);
DeleteObject(bitmap);
@@ -149,61 +138,52 @@ void pCanvas::paint() {
EndPaint(hwnd, &ps);
}
void pCanvas::rasterize() {
unsigned width = canvas.state.width;
unsigned height = canvas.state.height;
if(width == 0) width = widget.state.geometry.width;
if(height == 0) height = widget.state.geometry.height;
auto pCanvas::_rasterize() -> void {
if(mode == Mode::Color || mode == Mode::Gradient) {
width = self().geometry().width();
height = self().geometry().height();
} else {
width = state().size.width();
height = state().size.height();
}
if(width <= 0 || height <= 0) return;
if(width != surfaceWidth || height != surfaceHeight) release();
if(!surface) surface = new uint32_t[width * height];
pixels.reallocate(width * height);
if(canvas.state.mode == Canvas::Mode::Color) {
nall::image image;
image.allocate(width, height);
image.fill(canvas.state.color.argb());
memcpy(surface, image.data, image.size);
if(mode == Mode::Color) {
uint32_t color = state().color.value();
for(auto& pixel : pixels) pixel = color;
}
if(canvas.state.mode == Canvas::Mode::Gradient) {
nall::image image;
image.allocate(width, height);
image.gradient(
canvas.state.gradient[0].argb(), canvas.state.gradient[1].argb(), canvas.state.gradient[2].argb(), canvas.state.gradient[3].argb()
if(mode == Mode::Gradient) {
image fill;
fill.allocate(width, height);
fill.gradient(
state().gradient[0].value(), state().gradient[1].value(),
state().gradient[2].value(), state().gradient[3].value()
);
memcpy(surface, image.data, image.size);
memory::copy(pixels.data(), fill.data, fill.size);
}
if(canvas.state.mode == Canvas::Mode::Image) {
nall::image image = canvas.state.image;
image.scale(width, height);
image.transform(0, 32, 255u << 24, 255u << 16, 255u << 8, 255u << 0);
memcpy(surface, image.data, image.size);
if(mode == Mode::Icon) {
auto icon = state().icon;
icon.scale(width, height);
icon.transform(0, 32, 255u << 24, 255u << 16, 255u << 8, 255u << 0);
memory::copy(pixels.data(), icon.data, icon.size);
}
if(canvas.state.mode == Canvas::Mode::Data) {
if(width == canvas.state.width && height == canvas.state.height) {
memcpy(surface, canvas.state.data, width * height * sizeof(uint32_t));
} else {
memset(surface, 0x00, width * height * sizeof(uint32_t));
}
if(mode == Mode::Data) {
memory::copy(
pixels.data(), pixels.size() * sizeof(uint32_t),
state().data.data(), state().data.size() * sizeof(uint32_t)
);
}
surfaceWidth = width;
surfaceHeight = height;
}
void pCanvas::redraw() {
auto pCanvas::_redraw() -> void {
InvalidateRect(hwnd, 0, false);
}
void pCanvas::release() {
if(surface) {
delete[] surface;
surface = nullptr;
surfaceWidth = 0;
surfaceHeight = 0;
}
}
}
#endif

View File

@@ -0,0 +1,31 @@
#if defined(Hiro_Canvas)
namespace hiro {
struct pCanvas : pWidget {
Declare(Canvas, Widget)
auto minimumSize() const -> Size override;
auto setColor(Color color) -> void;
auto setData(Size size) -> void;
auto setDroppable(bool droppable) -> void;
auto setGeometry(Geometry geometry) -> void override;
auto setGradient(Color topLeft, Color topRight, Color bottomLeft, Color bottomRight) -> void;
auto setIcon(const image& icon) -> void;
auto update() -> void;
enum class Mode : unsigned { Color, Data, Gradient, Icon };
auto _paint() -> void;
auto _rasterize() -> void;
auto _redraw() -> void;
Mode mode = Mode::Color;
vector<uint32_t> pixels;
signed width = 0;
signed height = 0;
};
}
#endif

View File

@@ -1,84 +1,94 @@
namespace phoenix {
#if defined(Hiro_CheckButton)
Size pCheckButton::minimumSize() {
Size size = pFont::size(hfont, checkButton.state.text);
namespace hiro {
if(checkButton.state.orientation == Orientation::Horizontal) {
size.width += checkButton.state.image.width;
size.height = max(checkButton.state.image.height, size.height);
}
if(checkButton.state.orientation == Orientation::Vertical) {
size.width = max(checkButton.state.image.width, size.width);
size.height += checkButton.state.image.height;
}
return {size.width + 20, size.height + 10};
}
void pCheckButton::setChecked(bool checked) {
SendMessage(hwnd, BM_SETCHECK, (WPARAM)checked, 0);
}
void pCheckButton::setImage(const image& image, Orientation orientation) {
nall::image nallImage = image;
nallImage.transform(0, 32, 255u << 24, 255u << 16, 255u << 8, 255u << 0);
if(hbitmap) { DeleteObject(hbitmap); hbitmap = 0; }
if(himagelist) { ImageList_Destroy(himagelist); himagelist = 0; }
if(OsVersion() < WindowsVista) nallImage.alphaBlend(GetSysColor(COLOR_BTNFACE));
hbitmap = CreateBitmap(nallImage);
himagelist = ImageList_Create(nallImage.width, nallImage.height, ILC_COLOR32, 1, 0);
ImageList_Add(himagelist, hbitmap, NULL);
BUTTON_IMAGELIST list;
list.himl = himagelist;
switch(orientation) {
case Orientation::Horizontal: SetRect(&list.margin, 5, 0, 0, 0); list.uAlign = BUTTON_IMAGELIST_ALIGN_LEFT; break;
case Orientation::Vertical: SetRect(&list.margin, 0, 5, 0, 0); list.uAlign = BUTTON_IMAGELIST_ALIGN_TOP; break;
}
Button_SetImageList(hwnd, &list);
setText(checkButton.state.text);
}
void pCheckButton::setText(string text) {
if(text.empty()) {
SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) | BS_BITMAP);
} else {
SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) & ~BS_BITMAP);
}
SetWindowText(hwnd, utf16_t(text));
}
void pCheckButton::constructor() {
auto pCheckButton::construct() -> void {
hwnd = CreateWindow(L"BUTTON", L"",
WS_CHILD | WS_TABSTOP | BS_CHECKBOX | BS_PUSHLIKE,
0, 0, 0, 0, parentHwnd, (HMENU)id, GetModuleHandle(0), 0);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&checkButton);
setDefaultFont();
setChecked(checkButton.state.checked);
setImage(checkButton.state.image, checkButton.state.orientation);
//setText(checkButton.state.text);
synchronize();
0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
pWidget::_setState();
_setState();
setBordered(state().bordered);
setChecked(state().checked);
}
void pCheckButton::destructor() {
auto pCheckButton::destruct() -> void {
if(hbitmap) { DeleteObject(hbitmap); hbitmap = 0; }
if(himagelist) { ImageList_Destroy(himagelist); himagelist = 0; }
DestroyWindow(hwnd);
}
void pCheckButton::orphan() {
destructor();
constructor();
auto pCheckButton::minimumSize() -> Size {
auto size = pFont::size(hfont, state().text);
if(state().orientation == Orientation::Horizontal) {
size.setWidth(size.width() + state().icon.width);
size.setHeight(max(size.height(), state().icon.height));
}
if(state().orientation == Orientation::Vertical) {
size.setWidth(max(size.width(), state().icon.width));
size.setHeight(size.height() + state().icon.height);
}
return {size.width() + (state().text ? 20 : 10), size.height() + 10};
}
void pCheckButton::onToggle() {
checkButton.state.checked = !checkButton.state.checked;
setChecked(checkButton.state.checked);
if(checkButton.onToggle) checkButton.onToggle();
auto pCheckButton::setBordered(bool bordered) -> void {
}
auto pCheckButton::setChecked(bool checked) -> void {
SendMessage(hwnd, BM_SETCHECK, (WPARAM)checked, 0);
}
auto pCheckButton::setIcon(const image& icon) -> void {
_setState();
}
auto pCheckButton::setOrientation(Orientation orientation) -> void {
_setState();
}
auto pCheckButton::setText(const string& text) -> void {
_setState();
}
auto pCheckButton::onToggle() -> void {
state().checked = !state().checked;
setChecked(state().checked);
self().doToggle();
}
auto pCheckButton::_setState() -> void {
image icon = state().icon;
icon.transform(0, 32, 255u << 24, 255u << 16, 255u << 8, 255u << 0);
if(hbitmap) { DeleteObject(hbitmap); hbitmap = 0; }
if(himagelist) { ImageList_Destroy(himagelist); himagelist = 0; }
if(OsVersion() < WindowsVista) icon.alphaBlend(GetSysColor(COLOR_BTNFACE));
hbitmap = CreateBitmap(icon);
himagelist = ImageList_Create(icon.width, icon.height, ILC_COLOR32, 1, 0);
ImageList_Add(himagelist, hbitmap, NULL);
BUTTON_IMAGELIST list;
list.himl = himagelist;
switch(state().orientation) {
case Orientation::Horizontal: SetRect(&list.margin, 5, 0, 0, 0); list.uAlign = BUTTON_IMAGELIST_ALIGN_LEFT; break;
case Orientation::Vertical: SetRect(&list.margin, 0, 5, 0, 0); list.uAlign = BUTTON_IMAGELIST_ALIGN_TOP; break;
}
Button_SetImageList(hwnd, &list);
if(auto text = state().text) {
SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) &~ BS_BITMAP);
SetWindowText(hwnd, utf16_t(text));
} else {
SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) | BS_BITMAP);
SetWindowText(hwnd, L"");
}
}
}
#endif

View File

@@ -0,0 +1,25 @@
#if defined(Hiro_CheckButton)
namespace hiro {
struct pCheckButton : pWidget {
Declare(CheckButton, Widget)
auto minimumSize() -> Size;
auto setBordered(bool bordered) -> void;
auto setChecked(bool checked) -> void;
auto setIcon(const image& icon) -> void;
auto setOrientation(Orientation orientation) -> void;
auto setText(const string& text) -> void;
auto onToggle() -> void;
auto _setState() -> void;
HBITMAP hbitmap = 0;
HIMAGELIST himagelist = 0;
};
}
#endif

View File

@@ -1,44 +1,42 @@
namespace phoenix {
#if defined(Hiro_CheckLabel)
Size pCheckLabel::minimumSize() {
Size size = pFont::size(hfont, checkLabel.state.text);
return {size.width + 20, size.height + 4};
}
namespace hiro {
void pCheckLabel::setChecked(bool checked) {
SendMessage(hwnd, BM_SETCHECK, (WPARAM)checked, 0);
}
void pCheckLabel::setText(string text) {
SetWindowText(hwnd, utf16_t(text));
}
void pCheckLabel::constructor() {
auto pCheckLabel::construct() -> void {
hwnd = CreateWindow(
L"BUTTON", L"",
WS_CHILD | WS_TABSTOP | BS_CHECKBOX,
0, 0, 0, 0, parentHwnd, (HMENU)id, GetModuleHandle(0), 0
0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0
);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&checkLabel);
setDefaultFont();
setChecked(checkLabel.state.checked);
setText(checkLabel.state.text);
synchronize();
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
pWidget::_setState();
setChecked(state().checked);
setText(state().text);
}
void pCheckLabel::destructor() {
auto pCheckLabel::destruct() -> void {
DestroyWindow(hwnd);
}
void pCheckLabel::orphan() {
destructor();
constructor();
auto pCheckLabel::minimumSize() -> Size {
auto size = pFont::size(hfont, state().text);
return {size.width() + 20, size.height() + 4};
}
void pCheckLabel::onToggle() {
checkLabel.state.checked = !checkLabel.state.checked;
setChecked(checkLabel.state.checked);
if(checkLabel.onToggle) checkLabel.onToggle();
auto pCheckLabel::setChecked(bool checked) -> void {
SendMessage(hwnd, BM_SETCHECK, (WPARAM)checked, 0);
}
auto pCheckLabel::setText(const string& text) -> void {
SetWindowText(hwnd, utf16_t(text));
}
auto pCheckLabel::onToggle() -> void {
state().checked = !state().checked;
setChecked(state().checked);
self().doToggle();
}
}
#endif

View File

@@ -0,0 +1,17 @@
#if defined(Hiro_CheckLabel)
namespace hiro {
struct pCheckLabel : pWidget {
Declare(CheckLabel, Widget)
auto minimumSize() -> Size;
auto setChecked(bool checked) -> void;
auto setText(const string& text) -> void;
auto onToggle() -> void;
};
}
#endif

View File

@@ -0,0 +1,42 @@
#if defined(Hiro_ComboButton)
namespace hiro {
auto pComboButtonItem::construct() -> void {
}
auto pComboButtonItem::destruct() -> void {
}
auto pComboButtonItem::setIcon(const image& icon) -> void {
//unsupported
}
auto pComboButtonItem::setSelected() -> void {
if(auto parent = _parent()) {
parent->lock();
SendMessage(parent->hwnd, CB_SETCURSEL, self().offset(), 0);
parent->unlock();
}
}
auto pComboButtonItem::setText(const string& text) -> void {
if(auto parent = _parent()) {
parent->lock();
SendMessage(parent->hwnd, CB_DELETESTRING, self().offset(), 0);
SendMessage(parent->hwnd, CB_INSERTSTRING, self().offset(), (LPARAM)(wchar_t*)utf16_t(state().text));
if(state().selected) setSelected();
parent->unlock();
}
}
auto pComboButtonItem::_parent() -> maybe<pComboButton&> {
if(auto parent = self().parentComboButton()) {
if(auto self = parent->self()) return *self;
}
return nothing;
}
}
#endif

View File

@@ -0,0 +1,17 @@
#if defined(Hiro_ComboButton)
namespace hiro {
struct pComboButtonItem : pObject {
Declare(ComboButtonItem, Object)
auto setIcon(const image& icon) -> void;
auto setSelected() -> void;
auto setText(const string& text) -> void;
auto _parent() -> maybe<pComboButton&>;
};
}
#endif

View File

@@ -1,77 +1,67 @@
namespace phoenix {
#if defined(Hiro_ComboButton)
void pComboButton::append(string text) {
SendMessage(hwnd, CB_ADDSTRING, 0, (LPARAM)(wchar_t*)utf16_t(text));
if(SendMessage(hwnd, CB_GETCOUNT, 0, 0) == 1) setSelection(0);
}
namespace hiro {
Size pComboButton::minimumSize() {
unsigned maximumWidth = 0;
for(auto& text : comboButton.state.text) maximumWidth = max(maximumWidth, pFont::size(hfont, text).width);
return {maximumWidth + 24, pFont::size(hfont, " ").height + 10};
}
void pComboButton::remove(unsigned selection) {
locked = true;
SendMessage(hwnd, CB_DELETESTRING, selection, 0);
locked = false;
if(selection == comboButton.state.selection) comboButton.setSelection(0);
}
void pComboButton::reset() {
SendMessage(hwnd, CB_RESETCONTENT, 0, 0);
}
void pComboButton::setGeometry(Geometry geometry) {
//height = minimum drop-down list height; use CB_SETITEMHEIGHT to control actual widget height
pWidget::setGeometry({geometry.x, geometry.y, geometry.width, 1});
RECT rc;
GetWindowRect(hwnd, &rc);
unsigned adjustedHeight = geometry.height - ((rc.bottom - rc.top) - SendMessage(hwnd, CB_GETITEMHEIGHT, (WPARAM)-1, 0));
SendMessage(hwnd, CB_SETITEMHEIGHT, (WPARAM)-1, adjustedHeight);
}
void pComboButton::setSelection(unsigned selection) {
SendMessage(hwnd, CB_SETCURSEL, selection, 0);
}
void pComboButton::setText(unsigned selection, string text) {
locked = true;
SendMessage(hwnd, CB_DELETESTRING, selection, 0);
SendMessage(hwnd, CB_INSERTSTRING, selection, (LPARAM)(wchar_t*)utf16_t(text));
setSelection(comboButton.state.selection);
locked = false;
}
void pComboButton::constructor() {
auto pComboButton::construct() -> void {
hwnd = CreateWindow(
L"COMBOBOX", L"",
WS_CHILD | WS_TABSTOP | CBS_DROPDOWNLIST | CBS_HASSTRINGS,
0, 0, 0, 0,
parentHwnd, (HMENU)id, GetModuleHandle(0), 0
_parentHandle(), nullptr, GetModuleHandle(0), 0
);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&comboButton);
setDefaultFont();
for(auto& text : comboButton.state.text) append(text);
setSelection(comboButton.state.selection);
synchronize();
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
pWidget::_setState();
for(auto& item : state().items) append(item);
}
void pComboButton::destructor() {
auto pComboButton::destruct() -> void {
DestroyWindow(hwnd);
}
void pComboButton::orphan() {
destructor();
constructor();
auto pComboButton::append(sComboButtonItem item) -> void {
lock();
SendMessage(hwnd, CB_ADDSTRING, 0, (LPARAM)(wchar_t*)utf16_t(item->state.text));
if(item->state.selected) SendMessage(hwnd, CB_SETCURSEL, item->offset(), 0);
if(SendMessage(hwnd, CB_GETCURSEL, 0, 0) == CB_ERR) item->setSelected();
unlock();
}
void pComboButton::onChange() {
unsigned selection = SendMessage(hwnd, CB_GETCURSEL, 0, 0);
if(selection == comboButton.state.selection) return;
comboButton.state.selection = selection;
if(comboButton.onChange) comboButton.onChange();
auto pComboButton::minimumSize() const -> Size {
signed width = 0;
for(auto& item : state().items) {
width = max(width, pFont::size(hfont, item->state.text).width());
}
return {width + 24, pFont::size(hfont, " ").height() + 10};
}
auto pComboButton::remove(sComboButtonItem item) -> void {
lock();
SendMessage(hwnd, CB_DELETESTRING, item->offset(), 0);
if(item->state.selected) SendMessage(hwnd, CB_SETCURSEL, 0, 0);
unlock();
}
auto pComboButton::reset() -> void {
SendMessage(hwnd, CB_RESETCONTENT, 0, 0);
}
auto pComboButton::setGeometry(Geometry geometry) -> void {
//height = minimum drop-down list height; use CB_SETITEMHEIGHT to control actual widget height
pWidget::setGeometry({geometry.x(), geometry.y(), geometry.width(), 1});
RECT rc;
GetWindowRect(hwnd, &rc);
unsigned adjustedHeight = geometry.height() - ((rc.bottom - rc.top) - SendMessage(hwnd, CB_GETITEMHEIGHT, (WPARAM)-1, 0));
SendMessage(hwnd, CB_SETITEMHEIGHT, (WPARAM)-1, adjustedHeight);
}
auto pComboButton::onChange() -> void {
signed offset = SendMessage(hwnd, CB_GETCURSEL, 0, 0);
if(offset == CB_ERR) return;
for(auto& item : state().items) item->state.selected = false;
if(auto item = self().item(offset)) item->setSelected();
self().doChange();
}
}
#endif

View File

@@ -0,0 +1,19 @@
#if defined(Hiro_ComboButton)
namespace hiro {
struct pComboButton : pWidget {
Declare(ComboButton, Widget)
auto append(sComboButtonItem item) -> void;
auto minimumSize() const -> Size override;
auto remove(sComboButtonItem item) -> void;
auto reset() -> void;
auto setGeometry(Geometry geometry) -> void override;
auto onChange() -> void;
};
}
#endif

View File

@@ -1,56 +0,0 @@
namespace phoenix {
static LRESULT CALLBACK Console_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
Console& console = *(Console*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if(msg == WM_CHAR) {
if(console.p.keyPress(wparam)) return 0;
}
return console.p.windowProc(hwnd, msg, wparam, lparam);
}
void pConsole::print(string text) {
}
void pConsole::reset() {
}
void pConsole::setBackgroundColor(Color color) {
if(backgroundBrush) DeleteObject(backgroundBrush);
backgroundBrush = CreateSolidBrush(RGB(color.red, color.green, color.blue));
}
void pConsole::setForegroundColor(Color color) {
}
void pConsole::setPrompt(string prompt) {
}
void pConsole::constructor() {
hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE, L"EDIT", L"",
WS_CHILD | WS_TABSTOP | ES_READONLY | ES_MULTILINE | ES_WANTRETURN,
0, 0, 0, 0, parentHwnd, (HMENU)id, GetModuleHandle(0), 0
);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&console);
setDefaultFont();
setBackgroundColor(console.state.backgroundColor);
windowProc = (LRESULT CALLBACK (*)(HWND, UINT, LPARAM, WPARAM))GetWindowLongPtr(hwnd, GWLP_WNDPROC);
SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)Console_windowProc);
synchronize();
}
void pConsole::destructor() {
DestroyWindow(hwnd);
}
void pConsole::orphan() {
destructor();
constructor();
}
bool pConsole::keyPress(unsigned scancode) {
return false;
}
}

View File

@@ -1,50 +1,54 @@
namespace phoenix {
#if defined(Hiro_Frame)
void pFrame::setEnabled(bool enabled) {
if(frame.state.layout) frame.state.layout->setEnabled(frame.state.layout->enabled());
pWidget::setEnabled(enabled);
}
namespace hiro {
void pFrame::setGeometry(Geometry geometry) {
bool empty = frame.state.text.empty();
Size size = pFont::size(hfont, frame.state.text);
pWidget::setGeometry({
geometry.x, geometry.y - (empty ? size.height >> 1 : 0),
geometry.width, geometry.height + (empty ? size.height >> 1 : 0)
});
if(frame.state.layout == nullptr) return;
if(empty) size.height = 1;
geometry.x += 1, geometry.width -= 2;
geometry.y += size.height, geometry.height -= size.height + 2;
frame.state.layout->setGeometry(geometry);
}
void pFrame::setText(string text) {
SetWindowText(hwnd, utf16_t(text));
}
void pFrame::setVisible(bool visible) {
if(frame.state.layout) frame.state.layout->setVisible(frame.state.layout->visible());
pWidget::setVisible(visible);
}
void pFrame::constructor() {
auto pFrame::construct() -> void {
hwnd = CreateWindow(L"BUTTON", L"",
WS_CHILD | BS_GROUPBOX,
0, 0, 0, 0, parentHwnd, (HMENU)id, GetModuleHandle(0), 0);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&frame);
setDefaultFont();
setText(frame.state.text);
synchronize();
0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
pWidget::_setState();
setText(state().text);
}
void pFrame::destructor() {
auto pFrame::destruct() -> void {
DestroyWindow(hwnd);
}
void pFrame::orphan() {
destructor();
constructor();
auto pFrame::setEnabled(bool enabled) -> void {
if(auto layout = state().layout) layout->setEnabled(layout->enabled());
pWidget::setEnabled(enabled);
}
auto pFrame::setGeometry(Geometry geometry) -> void {
bool empty = !state().text;
auto size = pFont::size(hfont, state().text);
pWidget::setGeometry({
geometry.x(),
geometry.y() - (empty ? size.height() >> 1 : 0),
geometry.width(),
geometry.height() + (empty ? size.height() >> 1 : 0)
});
if(auto layout = state().layout) {
if(empty) size.setHeight(1);
layout->setGeometry({
geometry.x() + 1,
geometry.y() + size.height(),
geometry.width() - 2,
geometry.height() - (size.height() + 2)
});
}
}
auto pFrame::setText(const string& text) -> void {
SetWindowText(hwnd, utf16_t(text));
}
auto pFrame::setVisible(bool visible) -> void {
if(auto layout = state().layout) layout->setVisible(layout->visible());
pWidget::setVisible(visible);
}
}
#endif

View File

@@ -0,0 +1,16 @@
#if defined(Hiro_Frame)
namespace hiro {
struct pFrame : pWidget {
Declare(Frame, Widget)
auto setEnabled(bool enabled) -> void override;
auto setGeometry(Geometry geometry) -> void override;
auto setText(const string& text) -> void;
auto setVisible(bool visible) -> void override;
};
}
#endif

View File

@@ -1,30 +1,35 @@
namespace phoenix {
#if defined(Hiro_HexEdit)
static LRESULT CALLBACK HexEdit_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
HexEdit& hexEdit = *(HexEdit*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
namespace hiro {
static auto CALLBACK HexEdit_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> LRESULT {
auto object = (mObject*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if(!object) return DefWindowProc(hwnd, msg, wparam, lparam);
auto hexEdit = dynamic_cast<mHexEdit*>(object);
if(!hexEdit) return DefWindowProc(hwnd, msg, wparam, lparam);
auto self = hexEdit->self();
if(!self) return DefWindowProc(hwnd, msg, wparam, lparam);
switch(msg) {
case WM_KEYDOWN:
if(hexEdit.p.keyPress(wparam)) return 0;
if(self->keyPress(wparam)) return 0;
break;
case WM_MOUSEWHEEL: {
signed offset = -((int16_t)HIWORD(wparam) / WHEEL_DELTA);
hexEdit.p.scrollTo(hexEdit.p.scrollPosition() + offset);
self->scrollTo(self->scrollPosition() + offset);
return true;
}
case WM_SIZE: {
RECT rc;
GetClientRect(hexEdit.p.hwnd, &rc);
SetWindowPos(hexEdit.p.scrollBar, HWND_TOP, rc.right - 18, 0, 18, rc.bottom, SWP_SHOWWINDOW);
GetClientRect(self->hwnd, &rc);
SetWindowPos(self->scrollBar, HWND_TOP, rc.right - 18, 0, 18, rc.bottom, SWP_SHOWWINDOW);
break;
}
case WM_VSCROLL: {
SCROLLINFO info;
memset(&info, 0, sizeof(SCROLLINFO));
info.cbSize = sizeof(SCROLLINFO);
SCROLLINFO info{sizeof(SCROLLINFO)};
info.fMask = SIF_ALL;
GetScrollInfo((HWND)lparam, SB_CTL, &info);
switch(LOWORD(wparam)) {
@@ -41,43 +46,73 @@ static LRESULT CALLBACK HexEdit_windowProc(HWND hwnd, UINT msg, WPARAM wparam, L
SetScrollInfo((HWND)lparam, SB_CTL, &info, TRUE);
GetScrollInfo((HWND)lparam, SB_CTL, &info); //get clamped position
hexEdit.p.scrollTo(info.nPos);
return TRUE;
self->scrollTo(info.nPos);
return true;
}
}
return hexEdit.p.windowProc(hwnd, msg, wparam, lparam);
return self->windowProc(hwnd, msg, wparam, lparam);
}
void pHexEdit::setBackgroundColor(Color color) {
auto pHexEdit::construct() -> void {
hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE, L"EDIT", L"",
WS_CHILD | WS_TABSTOP | ES_AUTOHSCROLL | ES_READONLY | ES_MULTILINE | ES_WANTRETURN,
0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0
);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
scrollBar = CreateWindowEx(
0, L"SCROLLBAR", L"",
WS_VISIBLE | WS_CHILD | SBS_VERT,
0, 0, 0, 0, hwnd, nullptr, GetModuleHandle(0), 0
);
SetWindowLongPtr(scrollBar, GWLP_USERDATA, (LONG_PTR)&reference);
windowProc = (WindowProc)GetWindowLongPtr(hwnd, GWLP_WNDPROC);
SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)HexEdit_windowProc);
pWidget::_setState();
setBackgroundColor(state().backgroundColor);
setLength(state().length);
setOffset(state().offset);
update();
PostMessage(hwnd, EM_SETSEL, 10, 10);
}
auto pHexEdit::destruct() -> void {
DestroyWindow(hwnd);
}
auto pHexEdit::setBackgroundColor(Color color) -> void {
if(backgroundBrush) DeleteObject(backgroundBrush);
backgroundBrush = CreateSolidBrush(RGB(color.red, color.green, color.blue));
backgroundBrush = CreateSolidBrush(color ? CreateRGB(color) : GetSysColor(COLOR_WINDOW));
}
void pHexEdit::setColumns(unsigned columns) {
auto pHexEdit::setColumns(unsigned columns) -> void {
update();
}
void pHexEdit::setForegroundColor(Color color) {
auto pHexEdit::setForegroundColor(Color color) -> void {
}
void pHexEdit::setLength(unsigned length) {
SetScrollRange(scrollBar, SB_CTL, 0, rowsScrollable(), TRUE);
auto pHexEdit::setLength(unsigned length) -> void {
SetScrollRange(scrollBar, SB_CTL, 0, rowsScrollable(), true);
EnableWindow(scrollBar, rowsScrollable() > 0);
update();
}
void pHexEdit::setOffset(unsigned offset) {
SetScrollPos(scrollBar, SB_CTL, offset / hexEdit.state.columns, TRUE);
auto pHexEdit::setOffset(unsigned offset) -> void {
SetScrollPos(scrollBar, SB_CTL, offset / state().columns, true);
update();
}
void pHexEdit::setRows(unsigned rows) {
auto pHexEdit::setRows(unsigned rows) -> void {
update();
}
void pHexEdit::update() {
if(!hexEdit.onRead) {
auto pHexEdit::update() -> void {
if(!state().onRead) {
SetWindowText(hwnd, L"");
return;
}
@@ -85,16 +120,16 @@ void pHexEdit::update() {
unsigned cursorPosition = Edit_GetSel(hwnd);
string output;
unsigned offset = hexEdit.state.offset;
for(unsigned row = 0; row < hexEdit.state.rows; row++) {
unsigned offset = state().offset;
for(auto row : range(state().rows)) {
output.append(hex<8>(offset));
output.append(" ");
string hexdata;
string ansidata = " ";
for(unsigned column = 0; column < hexEdit.state.columns; column++) {
if(offset < hexEdit.state.length) {
uint8_t data = hexEdit.onRead(offset++);
for(auto column : range(state().columns)) {
if(offset < state().length) {
uint8_t data = self().doRead(offset++);
hexdata.append(hex<2>(data));
hexdata.append(" ");
ansidata.append(data >= 0x20 && data <= 0x7e ? (char)data : '.');
@@ -106,56 +141,19 @@ void pHexEdit::update() {
output.append(hexdata);
output.append(ansidata);
if(offset >= hexEdit.state.length) break;
if(row != hexEdit.state.rows - 1) output.append("\r\n");
if(offset >= state().length) break;
if(row != state().rows - 1) output.append("\r\n");
}
SetWindowText(hwnd, utf16_t(output));
Edit_SetSel(hwnd, LOWORD(cursorPosition), HIWORD(cursorPosition));
}
void pHexEdit::constructor() {
hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE, L"EDIT", L"",
WS_CHILD | WS_TABSTOP | ES_AUTOHSCROLL | ES_READONLY | ES_MULTILINE | ES_WANTRETURN,
0, 0, 0, 0, parentHwnd, (HMENU)id, GetModuleHandle(0), 0
);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&hexEdit);
scrollBar = CreateWindowEx(
0, L"SCROLLBAR", L"",
WS_VISIBLE | WS_CHILD | SBS_VERT,
0, 0, 0, 0, hwnd, (HMENU)id, GetModuleHandle(0), 0
);
SetWindowLongPtr(scrollBar, GWLP_USERDATA, (LONG_PTR)&hexEdit);
windowProc = (WindowProc)GetWindowLongPtr(hwnd, GWLP_WNDPROC);
SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)HexEdit_windowProc);
setDefaultFont();
setBackgroundColor(hexEdit.state.backgroundColor);
setLength(hexEdit.state.length);
setOffset(hexEdit.state.offset);
update();
PostMessage(hwnd, EM_SETSEL, 10, 10);
synchronize();
}
void pHexEdit::destructor() {
DestroyWindow(hwnd);
}
void pHexEdit::orphan() {
destructor();
constructor();
}
bool pHexEdit::keyPress(unsigned scancode) {
if(!hexEdit.onRead) return false;
if(!state().onRead) return false;
signed position = LOWORD(Edit_GetSel(hwnd));
signed lineWidth = 10 + (hexEdit.state.columns * 3) + 1 + hexEdit.state.columns + 2;
signed lineWidth = 10 + (state().columns * 3) + 1 + state().columns + 2;
signed cursorY = position / lineWidth;
signed cursorX = position % lineWidth;
@@ -179,18 +177,18 @@ bool pHexEdit::keyPress(unsigned scancode) {
if(scancode == VK_DOWN) {
if(cursorY >= rows() - 1) return true;
if(cursorY < hexEdit.state.rows - 1) return false;
if(cursorY < state().rows - 1) return false;
scrollTo(scrollPosition() + 1);
return true;
}
if(scancode == VK_PRIOR) {
scrollTo(scrollPosition() - hexEdit.state.rows);
scrollTo(scrollPosition() - state().rows);
return true;
}
if(scancode == VK_NEXT) {
scrollTo(scrollPosition() + hexEdit.state.rows);
scrollTo(scrollPosition() + state().rows);
return true;
}
@@ -207,12 +205,12 @@ bool pHexEdit::keyPress(unsigned scancode) {
//not on a space
bool cursorNibble = (cursorX % 3) == 1; //0 = high, 1 = low
cursorX /= 3;
if(cursorX < hexEdit.state.columns) {
if(cursorX < state().columns) {
//not in ANSI region
unsigned offset = hexEdit.state.offset + (cursorY * hexEdit.state.columns + cursorX);
unsigned offset = state().offset + (cursorY * state().columns + cursorX);
if(offset >= hexEdit.state.length) return false; //do not edit past end of data
uint8_t data = hexEdit.onRead(offset);
if(offset >= state().length) return false; //do not edit past end of data
uint8_t data = self().doRead(offset);
//write modified value
if(cursorNibble == 1) {
@@ -220,11 +218,11 @@ bool pHexEdit::keyPress(unsigned scancode) {
} else {
data = (data & 0x0f) | (scancode << 4);
}
if(hexEdit.onWrite) hexEdit.onWrite(offset, data);
self().doWrite(offset, data);
//auto-advance cursor to next nibble or byte
position++;
if(cursorNibble && cursorX != hexEdit.state.columns - 1) position++;
if(cursorNibble && cursorX != state().columns - 1) position++;
Edit_SetSel(hwnd, position, position);
//refresh output to reflect modified data
@@ -237,22 +235,24 @@ bool pHexEdit::keyPress(unsigned scancode) {
}
signed pHexEdit::rows() {
return (max(1u, hexEdit.state.length) + hexEdit.state.columns - 1) / hexEdit.state.columns;
return (max(1u, state().length) + state().columns - 1) / state().columns;
}
signed pHexEdit::rowsScrollable() {
return max(0u, rows() - hexEdit.state.rows);
return max(0u, rows() - state().rows);
}
signed pHexEdit::scrollPosition() {
return hexEdit.state.offset / hexEdit.state.columns;
return state().offset / state().columns;
}
void pHexEdit::scrollTo(signed position) {
if(position > rowsScrollable()) position = rowsScrollable();
if(position < 0) position = 0;
if(position == scrollPosition()) return;
hexEdit.setOffset(position * hexEdit.state.columns);
self().setOffset(position * state().columns);
}
}
#endif

View File

@@ -0,0 +1,29 @@
#if defined(Hiro_HexEdit)
namespace hiro {
struct pHexEdit : pWidget {
Declare(HexEdit, Widget)
auto setBackgroundColor(Color color) -> void;
auto setColumns(unsigned columns) -> void;
auto setForegroundColor(Color color) -> void;
auto setLength(unsigned length) -> void;
auto setOffset(unsigned offset) -> void;
auto setRows(unsigned rows) -> void;
auto update() -> void;
auto keyPress(unsigned key) -> bool;
auto rows() -> signed;
auto rowsScrollable() -> signed;
auto scrollPosition() -> signed;
auto scrollTo(signed position) -> void;
WindowProc windowProc = nullptr;
HWND scrollBar = nullptr;
HBRUSH backgroundBrush = nullptr;
};
}
#endif

View File

@@ -1,45 +1,42 @@
namespace phoenix {
#if defined(Hiro_HorizontalScroller)
Size pHorizontalScroller::minimumSize() {
return {0, 18};
}
namespace hiro {
void pHorizontalScroller::setLength(unsigned length) {
length += (length == 0);
SetScrollRange(hwnd, SB_CTL, 0, length - 1, TRUE);
horizontalScroller.setPosition(0);
}
void pHorizontalScroller::setPosition(unsigned position) {
SetScrollPos(hwnd, SB_CTL, position, TRUE);
}
void pHorizontalScroller::constructor() {
auto pHorizontalScroller::construct() -> void {
hwnd = CreateWindow(
L"SCROLLBAR", L"", WS_CHILD | WS_TABSTOP | SBS_HORZ,
0, 0, 0, 0, parentHwnd, (HMENU)id, GetModuleHandle(0), 0
0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0
);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&horizontalScroller);
unsigned position = horizontalScroller.state.position;
setLength(horizontalScroller.state.length);
horizontalScroller.setPosition(position);
synchronize();
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
pWidget::_setState();
setLength(state().length);
setPosition(state().position);
}
void pHorizontalScroller::destructor() {
auto pHorizontalScroller::destruct() -> void {
DestroyWindow(hwnd);
}
void pHorizontalScroller::orphan() {
destructor();
constructor();
auto pHorizontalScroller::minimumSize() const -> Size {
return {0, 18};
}
void pHorizontalScroller::onChange(WPARAM wparam) {
auto pHorizontalScroller::setLength(unsigned length) -> void {
length += (length == 0);
SetScrollRange(hwnd, SB_CTL, 0, length - 1, TRUE);
}
auto pHorizontalScroller::setPosition(unsigned position) -> void {
SetScrollPos(hwnd, SB_CTL, position, TRUE);
}
auto pHorizontalScroller::onChange(WPARAM wparam) -> void {
unsigned position = ScrollEvent(hwnd, wparam);
if(position == horizontalScroller.state.position) return;
horizontalScroller.state.position = position;
if(horizontalScroller.onChange) horizontalScroller.onChange();
if(position == state().position) return;
state().position = position;
self().doChange();
}
}
#endif

View File

@@ -0,0 +1,17 @@
#if defined(Hiro_HorizontalScroller)
namespace hiro {
struct pHorizontalScroller : pWidget {
Declare(HorizontalScroller, Widget)
auto minimumSize() const -> Size override;
auto setLength(unsigned length) -> void;
auto setPosition(unsigned position) -> void;
auto onChange(WPARAM wparam) -> void;
};
}
#endif

View File

@@ -1,47 +1,43 @@
namespace phoenix {
#if defined(Hiro_HorizontalSlider)
Size pHorizontalSlider::minimumSize() {
return {0, 25};
}
namespace hiro {
void pHorizontalSlider::setLength(unsigned length) {
length += (length == 0);
SendMessage(hwnd, TBM_SETRANGE, (WPARAM)true, (LPARAM)MAKELONG(0, length - 1));
SendMessage(hwnd, TBM_SETPAGESIZE, 0, (LPARAM)(length >> 3));
horizontalSlider.setPosition(0);
}
void pHorizontalSlider::setPosition(unsigned position) {
SendMessage(hwnd, TBM_SETPOS, (WPARAM)true, (LPARAM)position);
}
void pHorizontalSlider::constructor() {
auto pHorizontalSlider::construct() -> void {
hwnd = CreateWindow(
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, _parentHandle(), nullptr, GetModuleHandle(0), 0
);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&horizontalSlider);
unsigned position = horizontalSlider.state.position;
setLength(horizontalSlider.state.length);
horizontalSlider.setPosition(position);
synchronize();
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
pWidget::_setState();
setLength(state().length);
setPosition(state().position);
}
void pHorizontalSlider::destructor() {
auto pHorizontalSlider::destruct() -> void {
DestroyWindow(hwnd);
}
void pHorizontalSlider::orphan() {
destructor();
constructor();
auto pHorizontalSlider::minimumSize() const -> Size {
return {0, 25};
}
void pHorizontalSlider::onChange() {
auto pHorizontalSlider::setLength(unsigned length) -> void {
length += (length == 0);
SendMessage(hwnd, TBM_SETRANGE, (WPARAM)true, (LPARAM)MAKELONG(0, length - 1));
SendMessage(hwnd, TBM_SETPAGESIZE, 0, (LPARAM)(length >> 3));
}
auto pHorizontalSlider::setPosition(unsigned position) -> void {
SendMessage(hwnd, TBM_SETPOS, (WPARAM)true, (LPARAM)position);
}
auto pHorizontalSlider::onChange() -> void {
unsigned position = SendMessage(hwnd, TBM_GETPOS, 0, 0);
if(position == horizontalSlider.state.position) return;
horizontalSlider.state.position = position;
if(horizontalSlider.onChange) horizontalSlider.onChange();
if(position == state().position) return;
state().position = position;
self().doChange();
}
}
#endif

View File

@@ -0,0 +1,17 @@
#if defined(Hiro_HorizontalSlider)
namespace hiro {
struct pHorizontalSlider : pWidget {
Declare(HorizontalSlider, Widget)
auto minimumSize() const -> Size override;
auto setLength(unsigned length) -> void;
auto setPosition(unsigned position) -> void;
auto onChange() -> void;
};
}
#endif

View File

@@ -1,44 +1,45 @@
namespace phoenix {
#if defined(Hiro_Label)
Size pLabel::minimumSize() {
Size size = pFont::size(hfont, label.state.text);
return {size.width, size.height};
namespace hiro {
auto pLabel::construct() -> void {
hwnd = CreateWindow(L"hiroLabel", L"",
WS_CHILD,
0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
pWidget::_setState();
setText(state().text);
}
void pLabel::setText(string text) {
auto pLabel::destruct() -> void {
DestroyWindow(hwnd);
}
auto pLabel::minimumSize() const -> Size {
Size size = pFont::size(hfont, state().text);
return {size.width(), size.height()};
}
auto pLabel::setHorizontalAlignment(double alignment) -> void {
}
auto pLabel::setText(const string& text) -> void {
SetWindowText(hwnd, utf16_t(text));
InvalidateRect(hwnd, 0, false);
}
void pLabel::constructor() {
hwnd = CreateWindow(L"phoenix_label", L"",
WS_CHILD,
0, 0, 0, 0, parentHwnd, (HMENU)id, GetModuleHandle(0), 0);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&label);
setDefaultFont();
setText(label.state.text);
synchronize();
auto pLabel::setVerticalAlignment(double alignment) -> void {
}
void pLabel::destructor() {
DestroyWindow(hwnd);
}
void pLabel::orphan() {
destructor();
constructor();
}
static LRESULT CALLBACK Label_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
HWND parentHwnd = GetParent(hwnd);
Label* label = (Label*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if(label == nullptr) return DefWindowProc(hwnd, msg, wparam, lparam);
Window* window = (Window*)label->Sizable::state.window;
if(window == nullptr) return DefWindowProc(hwnd, msg, wparam, lparam);
static auto CALLBACK Label_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> LRESULT {
auto label = (mLabel*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if(!label) return DefWindowProc(hwnd, msg, wparam, lparam);
auto window = label->parentWindow(true);
if(!window) return DefWindowProc(hwnd, msg, wparam, lparam);
switch(msg) {
case WM_GETDLGCODE: return DLGC_STATIC | DLGC_WANTCHARS;
case WM_ERASEBKGND: return TRUE;
case WM_ERASEBKGND: return true;
case WM_PAINT: {
PAINTSTRUCT ps;
BeginPaint(hwnd, &ps);
@@ -46,7 +47,7 @@ static LRESULT CALLBACK Label_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPA
GetClientRect(hwnd, &rc);
DrawThemeParentBackground(hwnd, ps.hdc, &rc);
SetBkMode(ps.hdc, TRANSPARENT);
SelectObject(ps.hdc, ((Widget*)label)->p.hfont);
SelectObject(ps.hdc, label->self()->hfont);
unsigned length = GetWindowTextLength(hwnd);
wchar_t text[length + 1];
GetWindowText(hwnd, text, length + 1);
@@ -58,7 +59,7 @@ 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 false;
}
}
@@ -66,3 +67,5 @@ static LRESULT CALLBACK Label_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPA
}
}
#endif

View File

@@ -0,0 +1,16 @@
#if defined(Hiro_Label)
namespace hiro {
struct pLabel : pWidget {
Declare(Label, Widget)
auto minimumSize() const -> Size override;
auto setHorizontalAlignment(double alignment) -> void;
auto setText(const string& text) -> void;
auto setVerticalAlignment(double alignment) -> void;
};
}
#endif

View File

@@ -1,29 +1,54 @@
namespace phoenix {
#if defined(Hiro_LineEdit)
Size pLineEdit::minimumSize() {
Size size = pFont::size(hfont, lineEdit.state.text);
return {size.width + 12, size.height + 10};
namespace hiro {
auto pLineEdit::construct() -> void {
hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE, L"EDIT", L"",
WS_CHILD | WS_TABSTOP | ES_AUTOHSCROLL | ES_AUTOVSCROLL,
0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0
);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
pWidget::_setState();
setBackgroundColor(state().backgroundColor);
setEditable(state().editable);
setText(state().text);
}
void pLineEdit::setBackgroundColor(Color color) {
if(backgroundBrush) DeleteObject(backgroundBrush);
backgroundBrush = CreateSolidBrush(RGB(color.red, color.green, color.blue));
auto pLineEdit::destruct() -> void {
if(backgroundBrush) { DeleteObject(backgroundBrush); backgroundBrush = 0; }
DestroyWindow(hwnd);
}
void pLineEdit::setEditable(bool editable) {
auto pLineEdit::minimumSize() const -> Size {
auto size = pFont::size(hfont, state().text);
return {size.width() + 12, size.height() + 10};
}
auto pLineEdit::setBackgroundColor(Color color) -> void {
if(backgroundBrush) { DeleteObject(backgroundBrush); backgroundBrush = 0; }
backgroundBrush = CreateSolidBrush(color ? CreateRGB(color) : GetSysColor(COLOR_WINDOW));
}
auto pLineEdit::setEditable(bool editable) -> void {
SendMessage(hwnd, EM_SETREADONLY, editable == false, 0);
}
void pLineEdit::setForegroundColor(Color color) {
auto pLineEdit::setForegroundColor(Color color) -> void {
}
void pLineEdit::setText(string text) {
locked = true;
auto pLineEdit::setText(const string& text) -> void {
lock();
SetWindowText(hwnd, utf16_t(text));
locked = false;
unlock();
}
string pLineEdit::text() {
auto pLineEdit::onChange() -> void {
state().text = _text();
if(!locked()) self().doChange();
}
auto pLineEdit::_text() -> string {
unsigned length = GetWindowTextLength(hwnd);
wchar_t text[length + 1];
GetWindowText(hwnd, text, length + 1);
@@ -31,33 +56,6 @@ string pLineEdit::text() {
return (const char*)utf8_t(text);
}
void pLineEdit::constructor() {
hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE, L"EDIT", L"",
WS_CHILD | WS_TABSTOP | ES_AUTOHSCROLL | ES_AUTOVSCROLL,
0, 0, 0, 0, parentHwnd, (HMENU)id, GetModuleHandle(0), 0
);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&lineEdit);
setDefaultFont();
setBackgroundColor(lineEdit.state.backgroundColor);
setEditable(lineEdit.state.editable);
setText(lineEdit.state.text);
synchronize();
}
void pLineEdit::destructor() {
lineEdit.state.text = text();
DestroyWindow(hwnd);
}
void pLineEdit::orphan() {
destructor();
constructor();
}
void pLineEdit::onChange() {
if(locked) return;
if(lineEdit.onChange) lineEdit.onChange();
}
}
#endif

View File

@@ -0,0 +1,23 @@
#if defined(Hiro_LineEdit)
namespace hiro {
struct pLineEdit : pWidget {
Declare(LineEdit, Widget)
auto minimumSize() const -> Size;
auto setBackgroundColor(Color color) -> void;
auto setEditable(bool editable) -> void;
auto setForegroundColor(Color color) -> void;
auto setText(const string& text) -> void;
auto onChange() -> void;
auto _text() -> string;
HBRUSH backgroundBrush = nullptr;
};
}
#endif

View File

@@ -0,0 +1,49 @@
#if defined(Hiro_ListView)
namespace hiro {
auto pListViewCell::construct() -> void {
}
auto pListViewCell::destruct() -> void {
}
auto pListViewCell::setBackgroundColor(Color color) -> void {
}
auto pListViewCell::setForegroundColor(Color color) -> void {
}
auto pListViewCell::setIcon(const image& icon) -> void {
}
auto pListViewCell::setText(const string& text) -> void {
}
auto pListViewCell::_parent() -> maybe<pListViewItem&> {
if(auto parent = self().parentListViewItem()) {
if(auto self = parent->self()) return *self;
}
return nothing;
}
auto pListViewCell::_setState() -> void {
if(auto item = _parent()) {
if(auto parent = item->_parent()) {
parent->lock();
wchar_t text[] = L"";
LVITEM lvItem;
lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
lvItem.iItem = item->self().offset();
lvItem.iSubItem = self().offset();
lvItem.iImage = parent->self().columns();
lvItem.pszText = text;
ListView_SetItem(parent->hwnd, &lvItem);
parent->unlock();
}
}
}
}
#endif

View File

@@ -0,0 +1,19 @@
#if defined(Hiro_ListView)
namespace hiro {
struct pListViewCell : pObject {
Declare(ListViewCell, Object)
auto setBackgroundColor(Color color) -> void;
auto setForegroundColor(Color color) -> void;
auto setIcon(const image& icon) -> void;
auto setText(const string& text) -> void;
auto _parent() -> maybe<pListViewItem&>;
auto _setState() -> void;
};
}
#endif

View File

@@ -0,0 +1,81 @@
#if defined(Hiro_ListView)
namespace hiro {
auto pListViewColumn::construct() -> void {
}
auto pListViewColumn::destruct() -> void {
}
auto pListViewColumn::setActive() -> void {
//unsupported
}
auto pListViewColumn::setBackgroundColor(Color color) -> void {
}
auto pListViewColumn::setEditable(bool editable) -> void {
//unsupported
}
auto pListViewColumn::setExpandable(bool expandable) -> void {
}
auto pListViewColumn::setForegroundColor(Color color) -> void {
}
auto pListViewColumn::setHorizontalAlignment(double alignment) -> void {
_setState();
}
auto pListViewColumn::setIcon(const image& icon) -> void {
_setState();
}
auto pListViewColumn::setResizable(bool resizable) -> void {
_setState();
}
auto pListViewColumn::setText(const string& text) -> void {
_setState();
}
auto pListViewColumn::setVerticalAlignment(double alignment) -> void {
}
auto pListViewColumn::setWidth(signed width) -> void {
_setState();
}
auto pListViewColumn::_parent() -> maybe<pListView&> {
if(auto parent = self().parentListView()) {
if(auto self = parent->self()) return *self;
}
return nothing;
}
auto pListViewColumn::_setState() -> void {
if(auto parent = _parent()) {
parent->lock();
parent->_setIcons();
utf16_t text(state().text);
LVCOLUMN lvColumn;
lvColumn.mask = LVCF_FMT | LVCF_SUBITEM | LVCF_TEXT | LVCF_WIDTH;
lvColumn.fmt = LVCFMT_CENTER;
lvColumn.iSubItem = self().offset();
lvColumn.iImage = self().offset();
lvColumn.pszText = text;
lvColumn.cx = _width;
if(state().horizontalAlignment < 0.333) lvColumn.fmt = LVCFMT_LEFT;
if(state().horizontalAlignment > 0.666) lvColumn.fmt = LVCFMT_RIGHT;
if(state().icon) lvColumn.mask |= LVCF_IMAGE;
if(!state().resizable) lvColumn.fmt |= LVCFMT_FIXED_WIDTH;
ListView_SetColumn(parent->hwnd, self().offset(), &lvColumn);
parent->unlock();
}
}
}
#endif

View File

@@ -0,0 +1,28 @@
#if defined(Hiro_ListView)
namespace hiro {
struct pListViewColumn : pObject {
Declare(ListViewColumn, Object)
auto setActive() -> void;
auto setBackgroundColor(Color color) -> void;
auto setEditable(bool editable) -> void;
auto setExpandable(bool expandable) -> void;
auto setForegroundColor(Color color) -> void;
auto setHorizontalAlignment(double alignment) -> void;
auto setIcon(const image& icon) -> void;
auto setResizable(bool resizable) -> void;
auto setText(const string& text) -> void;
auto setVerticalAlignment(double alignment) -> void;
auto setWidth(signed width) -> void;
auto _parent() -> maybe<pListView&>;
auto _setState() -> void;
signed _width = 128; //computed width (via ListView::resizeColumns)
};
}
#endif

View File

@@ -0,0 +1,57 @@
#if defined(Hiro_ListView)
namespace hiro {
auto pListViewItem::construct() -> void {
}
auto pListViewItem::destruct() -> void {
}
auto pListViewItem::append(sListViewCell cell) -> void {
}
auto pListViewItem::remove(sListViewCell cell) -> void {
}
auto pListViewItem::setBackgroundColor(Color color) -> void {
}
auto pListViewItem::setChecked(bool checked) -> void {
if(auto parent = _parent()) {
parent->lock();
ListView_SetCheckState(parent->hwnd, self().offset(), checked);
parent->unlock();
}
}
auto pListViewItem::setFocused() -> void {
if(auto parent = _parent()) {
parent->lock();
ListView_SetItemState(parent->hwnd, self().offset(), LVIS_FOCUSED, LVIS_FOCUSED);
parent->unlock();
}
}
auto pListViewItem::setForegroundColor(Color color) -> void {
}
auto pListViewItem::setSelected(bool selected) -> void {
if(auto parent = _parent()) {
parent->lock();
unsigned state = selected ? LVIS_SELECTED : 0;
ListView_SetItemState(parent->hwnd, self().offset(), state, LVIS_SELECTED);
parent->unlock();
}
}
auto pListViewItem::_parent() -> maybe<pListView&> {
if(auto parent = self().parentListView()) {
if(auto self = parent->self()) return *self;
}
return nothing;
}
}
#endif

View File

@@ -0,0 +1,21 @@
#if defined(Hiro_ListView)
namespace hiro {
struct pListViewItem : pObject {
Declare(ListViewItem, Object)
auto append(sListViewCell cell) -> void;
auto remove(sListViewCell cell) -> void;
auto setBackgroundColor(Color color) -> void;
auto setChecked(bool checked) -> void;
auto setFocused() -> void;
auto setForegroundColor(Color color) -> void;
auto setSelected(bool selected) -> void;
auto _parent() -> maybe<pListView&>;
};
}
#endif

View File

@@ -1,288 +1,428 @@
namespace phoenix {
#if defined(Hiro_ListView)
unsigned ListView_GetColumnCount(HWND hwnd) {
unsigned count = 0;
LVCOLUMN column;
column.mask = LVCF_WIDTH;
while(ListView_GetColumn(hwnd, count++, &column));
return --count;
}
namespace hiro {
void ListView_SetImage(HWND hwnd, HIMAGELIST imageList, unsigned row, unsigned column, unsigned imageID) {
//if this is the first image assigned, set image list now
//do not set sooner, or image blocks will appear in a list with no images
if(ListView_GetImageList(hwnd, LVSIL_SMALL) != imageList) {
ListView_SetImageList(hwnd, imageList, LVSIL_SMALL);
}
LVITEM item;
item.mask = LVIF_IMAGE;
item.iItem = row;
item.iSubItem = column;
item.iImage = imageID;
ListView_SetItem(hwnd, &item);
}
void pListView::append(const lstring& list) {
wchar_t empty[] = L"";
unsigned row = ListView_GetItemCount(hwnd);
LVITEM item;
item.mask = LVIF_TEXT;
item.iItem = row;
item.iSubItem = 0;
item.pszText = empty;
locked = true;
ListView_InsertItem(hwnd, &item);
locked = false;
for(unsigned column = 0; column < list.size(); column++) {
utf16_t wtext(list(column, ""));
ListView_SetItemText(hwnd, row, column, wtext);
}
}
void pListView::autoSizeColumns() {
unsigned columns = ListView_GetColumnCount(hwnd);
for(unsigned n = 0; n < columns; n++) {
ListView_SetColumnWidth(hwnd, n, LVSCW_AUTOSIZE_USEHEADER);
}
}
void pListView::remove(unsigned selection) {
ListView_DeleteItem(hwnd, selection);
}
void pListView::reset() {
ListView_DeleteAllItems(hwnd);
buildImageList(); //free previously allocated images
}
void pListView::setBackgroundColor(Color color) {
ListView_SetBkColor(hwnd, RGB(color.red, color.green, color.blue));
}
void pListView::setCheckable(bool checkable) {
ListView_SetExtendedListViewStyle(hwnd, LVS_EX_FULLROWSELECT | LVS_EX_SUBITEMIMAGES | (checkable ? LVS_EX_CHECKBOXES : 0));
}
void pListView::setChecked(unsigned selection, bool checked) {
locked = true;
ListView_SetCheckState(hwnd, selection, checked);
locked = false;
}
void pListView::setForegroundColor(Color color) {
}
void pListView::setGeometry(Geometry geometry) {
pWidget::setGeometry(geometry);
autoSizeColumns();
}
void pListView::setHeaderText(const lstring& list) {
while(ListView_DeleteColumn(hwnd, 0));
lstring headers = list;
if(headers.size() == 0) headers.append(""); //must have at least one column
for(unsigned n = 0; n < headers.size(); n++) {
LVCOLUMN column;
column.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM;
column.fmt = LVCFMT_LEFT;
column.iSubItem = n;
utf16_t headerText(headers[n]);
column.pszText = headerText;
ListView_InsertColumn(hwnd, n, &column);
}
autoSizeColumns();
}
void pListView::setHeaderVisible(bool visible) {
SetWindowLong(
hwnd, GWL_STYLE,
(GetWindowLong(hwnd, GWL_STYLE) & ~LVS_NOCOLUMNHEADER) |
(visible ? 0 : LVS_NOCOLUMNHEADER)
);
}
void pListView::setImage(unsigned selection, unsigned position, const image& image) {
//assign existing image
for(unsigned n = 0; n < images.size(); n++) {
if(images[n] == image) {
imageMap(selection)(position) = n;
return ListView_SetImage(hwnd, imageList, selection, position, n);
}
}
//append and assign new image
imageMap(selection)(position) = images.size();
images.append(image);
ImageList_Append(imageList, image, 15);
ListView_SetImage(hwnd, imageList, selection, position, imageMap(selection)(position));
}
void pListView::setSelected(bool selected) {
locked = true;
lostFocus = false;
if(selected == false) {
ListView_SetItemState(hwnd, -1, 0, LVIS_FOCUSED | LVIS_SELECTED);
} else {
setSelection(listView.state.selection);
}
locked = false;
}
void pListView::setSelection(unsigned selection) {
locked = true;
lostFocus = false;
ListView_SetItemState(hwnd, selection, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
locked = false;
}
void pListView::setText(unsigned selection, unsigned position, string text) {
utf16_t wtext(text);
ListView_SetItemText(hwnd, selection, position, wtext);
}
void pListView::constructor() {
lostFocus = false;
auto pListView::construct() -> void {
hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE, WC_LISTVIEW, L"",
WS_CHILD | WS_TABSTOP | LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | LVS_NOCOLUMNHEADER,
0, 0, 0, 0, parentHwnd, (HMENU)id, GetModuleHandle(0), 0
WS_CHILD | WS_TABSTOP | LVS_REPORT | LVS_SHOWSELALWAYS,
0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0
);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&listView);
setDefaultFont();
setHeaderText(listView.state.headerText);
setBackgroundColor(listView.state.backgroundColor);
setHeaderVisible(listView.state.headerVisible);
setCheckable(listView.state.checkable);
for(auto& text : listView.state.text) append(text);
for(unsigned n = 0; n < listView.state.checked.size(); n++) setChecked(n, listView.state.checked[n]);
buildImageList();
if(listView.state.selected) setSelection(listView.state.selection);
autoSizeColumns();
synchronize();
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
ListView_SetExtendedListViewStyle(hwnd, LVS_EX_FULLROWSELECT | LVS_EX_SUBITEMIMAGES);
pWidget::_setState();
setBackgroundColor(state().backgroundColor);
setBatchable(state().batchable);
setCheckable(state().checkable);
setGridVisible(state().gridVisible);
setHeaderVisible(state().headerVisible);
setSortable(state().sortable);
_setIcons();
resizeColumns();
}
void pListView::destructor() {
auto pListView::destruct() -> void {
if(imageList) { ImageList_Destroy(imageList); imageList = 0; }
DestroyWindow(hwnd);
}
void pListView::orphan() {
destructor();
constructor();
auto pListView::append(sListViewColumn column) -> void {
lock();
wchar_t text[] = L"";
LVCOLUMN lvColumn;
lvColumn.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM;
lvColumn.fmt = LVCFMT_LEFT;
lvColumn.iSubItem = column->offset();
lvColumn.pszText = text;
ListView_InsertColumn(hwnd, column->offset(), &lvColumn);
if(auto self = column->self()) {
self->_setState();
}
resizeColumns();
unlock();
}
void pListView::buildImageList() {
auto& list = listView.state.image;
unsigned columns = listView.state.text.size();
unsigned rows = max(1u, listView.state.headerText.size());
ListView_SetImageList(hwnd, NULL, LVSIL_SMALL);
if(imageList) ImageList_Destroy(imageList);
imageList = ImageList_Create(15, 15, ILC_COLOR32, 1, 0);
imageMap.reset();
images.reset();
images.append(nall::image()); //empty icon for cells without an image assigned (I_IMAGENONE does not work)
//create a vector of unique images from all images used (many cells may use the same image)
for(unsigned y = 0; y < list.size(); y++) {
for(unsigned x = 0; x < list[y].size(); x++) {
bool found = false;
for(unsigned z = 0; z < images.size(); z++) {
if(list[y][x] == images[z]) {
found = true;
imageMap(y)(x) = z;
break;
}
}
if(found == false) {
imageMap(y)(x) = images.size();
images.append(list[y][x]);
}
auto pListView::append(sListViewItem item) -> void {
lock();
wchar_t text[] = L"";
LVITEM lvItem;
lvItem.mask = LVIF_TEXT;
lvItem.iItem = item->offset();
lvItem.iSubItem = 0;
lvItem.pszText = text;
ListView_InsertItem(hwnd, &lvItem);
if(auto self = item->self()) {
self->setChecked(item->state.checked);
self->setSelected(item->state.selected);
}
for(auto& cell : item->state.cells) {
if(auto self = cell->self()) {
self->_setState();
}
}
unlock();
}
//build image list
for(auto& imageItem : images) ImageList_Append(imageList, imageItem, 15);
if(images.size() <= 1) return;
//set images for all cells
for(unsigned y = 0; y < columns; y++) {
for(unsigned x = 0; x < rows; x++) {
ListView_SetImage(hwnd, imageList, y, x, imageMap(y)(x));
}
auto pListView::checkAll() -> void {
for(auto& item : state().items) {
item->self()->setChecked(true);
}
}
void pListView::onActivate(LPARAM lparam) {
LPNMLISTVIEW nmlistview = (LPNMLISTVIEW)lparam;
if(listView.state.text.empty() || !listView.state.selected) return;
//LVN_ITEMACTIVATE is not re-entrant until DispatchMessage() completes
//if(listView.onActivate) listView.onActivate();
PostMessage(parentHwnd, WM_APP + AppMessage::ListView_onActivate, 0, (LPARAM)&listView);
auto pListView::remove(sListViewColumn column) -> void {
lock();
ListView_DeleteColumn(hwnd, column->offset());
unlock();
}
void pListView::onChange(LPARAM lparam) {
LPNMLISTVIEW nmlistview = (LPNMLISTVIEW)lparam;
auto pListView::remove(sListViewItem item) -> void {
lock();
ListView_DeleteItem(hwnd, item->offset());
unlock();
}
auto pListView::reset() -> void {
lock();
ListView_DeleteAllItems(hwnd);
LVCOLUMN lvColumn{LVCF_WIDTH};
while(ListView_GetColumn(hwnd, 0, &lvColumn)) ListView_DeleteColumn(hwnd, 0);
_setIcons(); //free icon resources
unlock();
}
auto pListView::resizeColumns() -> void {
lock();
vector<signed> widths;
signed minimumWidth = 0;
signed expandable = 0;
for(auto column : range(state().columns)) {
signed width = _width(column);
widths.append(width);
minimumWidth += width;
if(state().columns[column]->expandable()) expandable++;
}
signed maximumWidth = self().geometry().width() - 4;
SCROLLBARINFO sbInfo{sizeof(SCROLLBARINFO)};
if(GetScrollBarInfo(hwnd, OBJID_VSCROLL, &sbInfo)) {
if(!(sbInfo.rgstate[0] & STATE_SYSTEM_INVISIBLE)) {
maximumWidth -= sbInfo.rcScrollBar.right - sbInfo.rcScrollBar.left;
}
}
signed expandWidth = 0;
if(expandable && maximumWidth > minimumWidth) {
expandWidth = (maximumWidth - minimumWidth) / expandable;
}
for(auto column : range(state().columns)) {
if(auto self = state().columns[column]->self()) {
signed width = widths[column];
if(self->state().expandable) width += expandWidth;
self->_width = width;
self->_setState();
}
}
unlock();
}
auto pListView::selectAll() -> void {
lock();
ListView_SetItemState(hwnd, -1, LVIS_SELECTED, LVIS_SELECTED);
unlock();
}
auto pListView::setBackgroundColor(Color color) -> void {
if(!color) color = {255, 255, 255};
ListView_SetBkColor(hwnd, RGB(color.red(), color.green(), color.blue()));
}
auto pListView::setBatchable(bool batchable) -> void {
auto style = GetWindowLong(hwnd, GWL_STYLE);
!batchable ? style |= LVS_SINGLESEL : style &=~ LVS_SINGLESEL;
SetWindowLong(hwnd, GWL_STYLE, style);
}
auto pListView::setCheckable(bool checkable) -> void {
auto style = ListView_GetExtendedListViewStyle(hwnd);
checkable ? style |= LVS_EX_CHECKBOXES : style &=~ LVS_EX_CHECKBOXES;
ListView_SetExtendedListViewStyle(hwnd, style);
}
auto pListView::setForegroundColor(Color color) -> void {
}
auto pListView::setGridVisible(bool visible) -> void {
//rendered via onCustomDraw
}
auto pListView::setHeaderVisible(bool visible) -> void {
auto style = GetWindowLong(hwnd, GWL_STYLE);
!visible ? style |= LVS_NOCOLUMNHEADER : style &=~ LVS_NOCOLUMNHEADER;
SetWindowLong(hwnd, GWL_STYLE, style);
}
auto pListView::setSortable(bool sortable) -> void {
auto style = GetWindowLong(hwnd, GWL_STYLE);
!sortable ? style |= LVS_NOSORTHEADER : style &=~ LVS_NOSORTHEADER;
SetWindowLong(hwnd, GWL_STYLE, style);
}
auto pListView::uncheckAll() -> void {
for(auto& item : state().items) {
item->self()->setChecked(false);
}
}
auto pListView::unselectAll() -> void {
lock();
ListView_SetItemState(hwnd, -1, 0, LVIS_FOCUSED | LVIS_SELECTED);
unlock();
}
auto pListView::onActivate(LPARAM lparam) -> void {
auto nmlistview = (LPNMLISTVIEW)lparam;
if(ListView_GetSelectedCount(hwnd) == 0) return;
if(!locked()) {
//LVN_ITEMACTIVATE is not re-entrant until DispatchMessage() completes
//thus, we don't call self().doActivate() here
PostMessageOnce(_parentHandle(), AppMessage::ListView_onActivate, 0, (LPARAM)&reference);
}
}
auto pListView::onChange(LPARAM lparam) -> void {
auto nmlistview = (LPNMLISTVIEW)lparam;
if(!(nmlistview->uChanged & LVIF_STATE)) return;
unsigned selection = nmlistview->iItem;
unsigned imagemask = ((nmlistview->uNewState & LVIS_STATEIMAGEMASK) >> 12) - 1;
if(imagemask == 0 || imagemask == 1) {
if(!locked) {
listView.state.checked[selection] = !listView.state.checked[selection];
if(listView.onToggle) listView.onToggle(selection);
bool modified = false;
for(auto& item : state().items) {
bool selected = ListView_GetItemState(hwnd, item->offset(), LVIS_SELECTED) & LVIS_SELECTED;
if(item->state.selected != selected) {
modified = true;
item->state.selected = selected;
}
}
if(modified && !locked()) {
//state event change messages are sent for every item
//so when doing a batch select/deselect; this can generate several messages
//we use a delayed AppMessage so that only one callback event is fired off
PostMessageOnce(_parentHandle(), AppMessage::ListView_onChange, 0, (LPARAM)&reference);
}
unsigned row = nmlistview->iItem;
unsigned mask = ((nmlistview->uNewState & LVIS_STATEIMAGEMASK) >> 12) - 1;
if((mask == 0 || mask == 1) && !locked()) {
if(auto item = self().item(row)) {
item->state.checked = !item->state.checked;
self().doToggle(item);
}
} else if((nmlistview->uOldState & LVIS_FOCUSED) && !(nmlistview->uNewState & LVIS_FOCUSED)) {
lostFocus = true;
listView.state.selected = false;
listView.state.selection = 0;
} else if(!(nmlistview->uOldState & LVIS_SELECTED) && (nmlistview->uNewState & LVIS_SELECTED)) {
lostFocus = false;
listView.state.selected = true;
listView.state.selection = selection;
if(!locked && listView.onChange) listView.onChange();
} else if(!lostFocus && !listView.state.selected) {
lostFocus = false;
listView.state.selected = false;
listView.state.selection = 0;
if(!locked && listView.onChange) listView.onChange();
} else if(listView.selected() && ListView_GetSelectedCount(hwnd) == 0) {
listView.state.selected = false;
listView.state.selection = 0;
if(!locked && listView.onChange) listView.onChange();
}
}
LRESULT pListView::onCustomDraw(LPARAM lparam) {
LPNMLVCUSTOMDRAW lvcd = (LPNMLVCUSTOMDRAW)lparam;
auto pListView::onContext(LPARAM lparam) -> void {
auto nmitemactivate = (LPNMITEMACTIVATE)lparam;
return self().doContext();
}
auto pListView::onCustomDraw(LPARAM lparam) -> LRESULT {
auto lvcd = (LPNMLVCUSTOMDRAW)lparam;
switch(lvcd->nmcd.dwDrawStage) {
case CDDS_PREPAINT: {
return CDRF_NOTIFYITEMDRAW;
}
case CDDS_ITEMPREPAINT: {
Color& background = listView.state.backgroundColor;
Color& foreground = listView.state.foregroundColor;
lvcd->clrText = RGB(foreground.red, foreground.green, foreground.blue);
lvcd->clrTextBk = RGB(background.red, background.green, background.blue);
if(listView.state.headerText.size() >= 2 && lvcd->nmcd.dwItemSpec % 2) {
//draw alternating row colors if there are two or more columns
lvcd->clrTextBk = RGB(max(0, (signed)background.red - 17), max(0, (signed)background.green - 17), max(0, (signed)background.blue - 17));
default: return CDRF_DODEFAULT;
case CDDS_PREPAINT: return CDRF_NOTIFYITEMDRAW;
case CDDS_ITEMPREPAINT: return CDRF_NOTIFYSUBITEMDRAW | CDRF_NOTIFYPOSTPAINT;
case CDDS_ITEMPREPAINT | CDDS_SUBITEM: return CDRF_SKIPDEFAULT;
case CDDS_ITEMPOSTPAINT: {
HDC hdc = lvcd->nmcd.hdc;
HDC hdcSource = CreateCompatibleDC(hdc);
unsigned row = lvcd->nmcd.dwItemSpec;
for(auto column : range(state().columns)) {
RECT rc, rcLabel;
ListView_GetSubItemRect(hwnd, row, column, LVIR_BOUNDS, &rc);
ListView_GetSubItemRect(hwnd, row, column, LVIR_LABEL, &rcLabel);
rc.right = rcLabel.right; //bounds of column 0 returns width of entire item
signed iconSize = rc.bottom - rc.top - 1;
bool checked = state().items(row)->state.checked;
bool selected = state().items(row)->state.selected;
HBRUSH brush = CreateSolidBrush(selected ? GetSysColor(COLOR_HIGHLIGHT) : CreateRGB(_backgroundColor(row, column)));
FillRect(hdc, &rc, brush);
DeleteObject(brush);
if(state().checkable && column == 0) {
if(auto htheme = OpenThemeData(hwnd, L"BUTTON")) {
unsigned state = checked ? CBS_CHECKEDNORMAL : CBS_UNCHECKEDNORMAL;
SIZE size;
GetThemePartSize(htheme, hdc, BP_CHECKBOX, state, NULL, TS_TRUE, &size);
signed center = max(0, (rc.bottom - rc.top - size.cy) / 2);
RECT rd{rc.left + center, rc.top + center, rc.left + center + size.cx, rc.top + center + size.cy};
DrawThemeBackground(htheme, hdc, BP_CHECKBOX, state, &rd, NULL);
CloseThemeData(htheme);
}
rc.left += iconSize + 2;
} else {
rc.left += 2;
}
auto cell = self().item(row)->cell(column);
if(!cell) continue;
if(auto icon = cell->state.icon) {
icon.scale(iconSize, iconSize);
icon.transform(0, 32, 255u << 24, 255u << 16, 255u << 8, 255u << 0);
auto bitmap = CreateBitmap(icon);
SelectBitmap(hdcSource, bitmap);
BLENDFUNCTION blend{AC_SRC_OVER, 0, (BYTE)(selected ? 128 : 255), AC_SRC_ALPHA};
AlphaBlend(hdc, rc.left, rc.top, iconSize, iconSize, hdcSource, 0, 0, iconSize, iconSize, blend);
DeleteObject(bitmap);
rc.left += iconSize + 2;
}
if(auto text = cell->state.text) {
auto halign = state().columns(column)->state.horizontalAlignment;
auto valign = state().columns(column)->state.verticalAlignment;
utf16_t wText(text);
SetBkMode(hdc, TRANSPARENT);
SetTextColor(hdc, selected ? GetSysColor(COLOR_HIGHLIGHTTEXT) : CreateRGB(_foregroundColor(row, column)));
auto style = DT_SINGLELINE | DT_END_ELLIPSIS;
style |= halign < 0.333 ? DT_LEFT : halign > 0.666 ? DT_RIGHT : DT_CENTER;
style |= valign < 0.333 ? DT_TOP : valign > 0.666 ? DT_BOTTOM : DT_VCENTER;
rc.right -= 2;
auto font = pFont::create(_font(row, column));
SelectObject(hdc, font);
DrawText(hdc, wText, -1, &rc, style);
DeleteObject(font);
}
if(state().gridVisible) {
ListView_GetSubItemRect(hwnd, row, column, LVIR_BOUNDS, &rc);
rc.top = rc.bottom - 1;
FillRect(hdc, &rc, (HBRUSH)GetStockObject(LTGRAY_BRUSH));
ListView_GetSubItemRect(hwnd, row, column, LVIR_LABEL, &rc);
rc.left = rc.right - 1;
FillRect(hdc, &rc, (HBRUSH)GetStockObject(LTGRAY_BRUSH));
}
}
return CDRF_DODEFAULT;
DeleteDC(hdcSource);
return CDRF_SKIPDEFAULT;
}
default: {
return CDRF_DODEFAULT;
}
}
}
auto pListView::onSort(LPARAM lparam) -> void {
auto nmlistview = (LPNMLISTVIEW)lparam;
self().doSort(self().column(nmlistview->iSubItem));
}
auto pListView::_backgroundColor(unsigned _row, unsigned _column) -> Color {
if(auto item = self().item(_row)) {
if(auto cell = item->cell(_column)) {
if(auto color = cell->backgroundColor()) return color;
}
if(auto color = item->backgroundColor()) return color;
}
if(auto column = self().column(_column)) {
if(auto color = column->backgroundColor()) return color;
}
if(auto color = self().backgroundColor()) return color;
if(state().columns.size() >= 2 && _row % 2) return {240, 240, 240};
return {255, 255, 255};
}
auto pListView::_cellWidth(unsigned _row, unsigned _column) -> unsigned {
unsigned width = 6;
if(state().checkable && _column == 0) width += 16 + 2;
if(auto item = self().item(_row)) {
if(auto cell = item->cell(_column)) {
if(auto& icon = cell->state.icon) {
width += 16 + 2;
}
if(auto& text = cell->state.text) {
width += Font::size(_font(_row, _column), text).width();
}
}
}
return width;
}
auto pListView::_columnWidth(unsigned _column) -> unsigned {
unsigned width = 12;
if(auto column = self().column(_column)) {
if(auto& icon = column->state.icon) {
width += 16 + 12; //yes; icon spacing in column headers is excessive
}
if(auto& text = column->state.text) {
width += Font::size(self().font(true), text).width();
}
}
return width;
}
auto pListView::_font(unsigned _row, unsigned _column) -> string {
if(auto item = self().item(_row)) {
if(auto cell = item->cell(_column)) {
if(auto font = cell->font()) return font;
}
if(auto font = item->font()) return font;
}
if(auto column = self().column(_column)) {
if(auto font = column->font()) return font;
}
if(auto font = self().font(true)) return font;
return Font::sans(8);
}
auto pListView::_foregroundColor(unsigned _row, unsigned _column) -> Color {
if(auto item = self().item(_row)) {
if(auto cell = item->cell(_column)) {
if(auto color = cell->foregroundColor()) return color;
}
if(auto color = item->foregroundColor()) return color;
}
if(auto column = self().column(_column)) {
if(auto color = column->foregroundColor()) return color;
}
if(auto color = self().foregroundColor()) return color;
return {0, 0, 0};
}
auto pListView::_setIcons() -> void {
ListView_SetImageList(hwnd, NULL, LVSIL_SMALL);
if(imageList) ImageList_Destroy(imageList);
imageList = ImageList_Create(16, 16, ILC_COLOR32, 1, 0);
ListView_SetImageList(hwnd, imageList, LVSIL_SMALL);
for(auto column : range(state().columns)) {
auto icon = state().columns(column)->state.icon;
if(icon) {
icon.scale(16, 16);
icon.transform(0, 32, 255u << 24, 255u << 16, 255u << 8, 255u << 0);
} else {
icon.allocate(16, 16);
icon.fill(0x00ffffff);
}
auto bitmap = CreateBitmap(icon);
ImageList_Add(imageList, bitmap, NULL);
DeleteObject(bitmap);
}
//empty icon used for ListViewItems (drawn manually via onCustomDraw)
image icon;
icon.allocate(16, 16);
icon.fill(0x00ffffff);
auto bitmap = CreateBitmap(icon);
ImageList_Add(imageList, bitmap, NULL);
DeleteObject(bitmap);
}
auto pListView::_width(unsigned column) -> unsigned {
if(auto width = state().columns[column]->width()) return width;
unsigned width = 1;
if(state().headerVisible) {
width = max(width, _columnWidth(column));
}
for(auto row : range(state().items)) {
width = max(width, _cellWidth(row, column));
}
return width;
}
}
#endif

View File

@@ -0,0 +1,47 @@
#if defined(Hiro_ListView)
namespace hiro {
struct pListView : pWidget {
Declare(ListView, Widget)
auto append(sListViewColumn column) -> void;
auto append(sListViewItem item) -> void;
auto checkAll() -> void;
auto remove(sListViewColumn column) -> void;
auto remove(sListViewItem item) -> void;
auto reset() -> void;
auto resizeColumns() -> void;
auto selectAll() -> void;
auto setBackgroundColor(Color color) -> void;
auto setBatchable(bool batchable) -> void;
auto setCheckable(bool checkable) -> void;
auto setChecked(bool checked) -> void;
auto setForegroundColor(Color color) -> void;
auto setGridVisible(bool visible) -> void;
auto setHeaderVisible(bool visible) -> void;
auto setSortable(bool sortable) -> void;
auto uncheckAll() -> void;
auto unselectAll() -> void;
auto onActivate(LPARAM lparam) -> void;
auto onChange(LPARAM lparam) -> void;
auto onContext(LPARAM lparam) -> void;
auto onCustomDraw(LPARAM lparam) -> LRESULT;
auto onSort(LPARAM lparam) -> void;
auto _backgroundColor(unsigned row, unsigned column) -> Color;
auto _cellWidth(unsigned row, unsigned column) -> unsigned;
auto _columnWidth(unsigned column) -> unsigned;
auto _font(unsigned row, unsigned column) -> string;
auto _foregroundColor(unsigned row, unsigned column) -> Color;
auto _setIcons() -> void;
auto _width(unsigned column) -> unsigned;
HIMAGELIST imageList = 0;
vector<image> icons;
};
}
#endif

View File

@@ -1,31 +1,30 @@
namespace phoenix {
#if defined(Hiro_ProgressBar)
Size pProgressBar::minimumSize() {
return {0, 23};
}
namespace hiro {
void pProgressBar::setPosition(unsigned position) {
SendMessage(hwnd, PBM_SETPOS, (WPARAM)position, 0);
}
void pProgressBar::constructor() {
auto pProgressBar::construct() -> void {
hwnd = CreateWindow(PROGRESS_CLASS, L"",
WS_CHILD | PBS_SMOOTH,
0, 0, 0, 0, parentHwnd, (HMENU)id, GetModuleHandle(0), 0);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&progressBar);
0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
pWidget::_setState();
SendMessage(hwnd, PBM_SETRANGE, 0, MAKELPARAM(0, 100));
SendMessage(hwnd, PBM_SETSTEP, MAKEWPARAM(1, 0), 0);
setPosition(progressBar.state.position);
synchronize();
setPosition(state().position);
}
void pProgressBar::destructor() {
auto pProgressBar::destruct() -> void {
DestroyWindow(hwnd);
}
void pProgressBar::orphan() {
destructor();
constructor();
auto pProgressBar::minimumSize() const -> Size {
return {0, 23};
}
auto pProgressBar::setPosition(unsigned position) -> void {
SendMessage(hwnd, PBM_SETPOS, (WPARAM)position, 0);
}
}
#endif

View File

@@ -0,0 +1,14 @@
#if defined(Hiro_ProgressBar)
namespace hiro {
struct pProgressBar : pWidget {
Declare(ProgressBar, Widget)
auto minimumSize() const -> Size override;
auto setPosition(unsigned position) -> void;
};
}
#endif

View File

@@ -1,89 +1,107 @@
namespace phoenix {
#if defined(Hiro_RadioButton)
Size pRadioButton::minimumSize() {
Size size = pFont::size(hfont, radioButton.state.text);
namespace hiro {
if(radioButton.state.orientation == Orientation::Horizontal) {
size.width += radioButton.state.image.width;
size.height = max(radioButton.state.image.height, size.height);
}
if(radioButton.state.orientation == Orientation::Vertical) {
size.width = max(radioButton.state.image.width, size.width);
size.height += radioButton.state.image.height;
}
return {size.width + 20, size.height + 10};
}
void pRadioButton::setChecked() {
for(auto& item : radioButton.state.group) {
SendMessage(item.p.hwnd, BM_SETCHECK, (WPARAM)(&item == &radioButton), 0);
}
}
void pRadioButton::setGroup(const group<RadioButton>& group) {
}
void pRadioButton::setImage(const image& image, Orientation orientation) {
nall::image nallImage = image;
nallImage.transform(0, 32, 255u << 24, 255u << 16, 255u << 8, 255u << 0);
if(hbitmap) { DeleteObject(hbitmap); hbitmap = 0; }
if(himagelist) { ImageList_Destroy(himagelist); himagelist = 0; }
if(OsVersion() < WindowsVista) nallImage.alphaBlend(GetSysColor(COLOR_BTNFACE));
hbitmap = CreateBitmap(nallImage);
himagelist = ImageList_Create(nallImage.width, nallImage.height, ILC_COLOR32, 1, 0);
ImageList_Add(himagelist, hbitmap, NULL);
BUTTON_IMAGELIST list;
list.himl = himagelist;
switch(orientation) {
case Orientation::Horizontal: SetRect(&list.margin, 5, 0, 0, 0); list.uAlign = BUTTON_IMAGELIST_ALIGN_LEFT; break;
case Orientation::Vertical: SetRect(&list.margin, 0, 5, 0, 0); list.uAlign = BUTTON_IMAGELIST_ALIGN_TOP; break;
}
Button_SetImageList(hwnd, &list);
setText(radioButton.state.text);
}
void pRadioButton::setText(string text) {
if(text.empty()) {
SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) | BS_BITMAP);
} else {
SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) & ~BS_BITMAP);
}
SetWindowText(hwnd, utf16_t(text));
}
void pRadioButton::constructor() {
auto pRadioButton::construct() -> void {
hwnd = CreateWindow(L"BUTTON", L"",
WS_CHILD | WS_TABSTOP | BS_CHECKBOX | BS_PUSHLIKE,
0, 0, 0, 0, parentHwnd, (HMENU)id, GetModuleHandle(0), 0);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&radioButton);
setDefaultFont();
if(radioButton.state.checked) setChecked();
setImage(radioButton.state.image, radioButton.state.orientation);
//setText(radioButton.state.text);
synchronize();
0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
pWidget::_setState();
_setState();
setBordered(state().bordered);
if(state().checked) setChecked();
}
void pRadioButton::destructor() {
auto pRadioButton::destruct() -> void {
if(hbitmap) { DeleteObject(hbitmap); hbitmap = 0; }
if(himagelist) { ImageList_Destroy(himagelist); himagelist = 0; }
DestroyWindow(hwnd);
}
void pRadioButton::orphan() {
destructor();
constructor();
auto pRadioButton::minimumSize() -> Size {
auto size = pFont::size(hfont, state().text);
if(state().orientation == Orientation::Horizontal) {
size.setWidth(size.width() + state().icon.width);
size.setHeight(max(size.height(), state().icon.height));
}
if(state().orientation == Orientation::Vertical) {
size.setWidth(max(size.width(), state().icon.width));
size.setHeight(size.height() + state().icon.height);
}
return {size.width() + (state().text ? 20 : 10), size.height() + 10};
}
auto pRadioButton::setBordered(bool bordered) -> void {
}
auto pRadioButton::setChecked() -> void {
if(auto group = self().group()) {
for(auto& weak : group->state.objects) {
if(auto object = weak.acquire()) {
if(auto radioButton = dynamic_cast<mRadioButton*>(object.data())) {
if(auto self = radioButton->self()) {
SendMessage(self->hwnd, BM_SETCHECK, (WPARAM)(&self->reference == &reference), 0);
}
}
}
}
}
}
auto pRadioButton::setGroup(sGroup group) -> void {
}
auto pRadioButton::setIcon(const image& icon) -> void {
_setState();
}
auto pRadioButton::setOrientation(Orientation orientation) -> void {
_setState();
}
void pRadioButton::setText(const string& text) {
_setState();
}
void pRadioButton::onActivate() {
if(radioButton.state.checked) return;
radioButton.setChecked();
if(radioButton.onActivate) radioButton.onActivate();
if(state().checked) return;
self().setChecked();
self().doActivate();
}
auto pRadioButton::_setState() -> void {
image icon = state().icon;
icon.transform(0, 32, 255u << 24, 255u << 16, 255u << 8, 255u << 0);
if(hbitmap) { DeleteObject(hbitmap); hbitmap = 0; }
if(himagelist) { ImageList_Destroy(himagelist); himagelist = 0; }
if(OsVersion() < WindowsVista) icon.alphaBlend(GetSysColor(COLOR_BTNFACE));
hbitmap = CreateBitmap(icon);
himagelist = ImageList_Create(icon.width, icon.height, ILC_COLOR32, 1, 0);
ImageList_Add(himagelist, hbitmap, NULL);
BUTTON_IMAGELIST list;
list.himl = himagelist;
switch(state().orientation) {
case Orientation::Horizontal: SetRect(&list.margin, 5, 0, 0, 0); list.uAlign = BUTTON_IMAGELIST_ALIGN_LEFT; break;
case Orientation::Vertical: SetRect(&list.margin, 0, 5, 0, 0); list.uAlign = BUTTON_IMAGELIST_ALIGN_TOP; break;
}
Button_SetImageList(hwnd, &list);
if(auto text = state().text) {
SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) &~ BS_BITMAP);
SetWindowText(hwnd, utf16_t(text));
} else {
SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) | BS_BITMAP);
SetWindowText(hwnd, L"");
}
}
}
#endif

View File

@@ -0,0 +1,26 @@
#if defined(Hiro_RadioButton)
namespace hiro {
struct pRadioButton : pWidget {
Declare(RadioButton, Widget)
auto minimumSize() -> Size;
auto setBordered(bool bordered) -> void;
auto setChecked() -> void;
auto setGroup(sGroup group) -> void override;
auto setIcon(const image& icon) -> void;
auto setOrientation(Orientation orientation) -> void;
auto setText(const string& text) -> void;
auto onActivate() -> void;
auto _setState() -> void;
HBITMAP hbitmap = 0;
HIMAGELIST himagelist = 0;
};
}
#endif

View File

@@ -1,49 +1,55 @@
namespace phoenix {
#if defined(Hiro_RadioLabel)
Size pRadioLabel::minimumSize() {
Size size = pFont::size(hfont, radioLabel.state.text);
return {size.width + 20, size.height + 4};
}
namespace hiro {
void pRadioLabel::setChecked() {
for(auto& item : radioLabel.state.group) {
SendMessage(item.p.hwnd, BM_SETCHECK, (WPARAM)(&item == &radioLabel), 0);
}
}
void pRadioLabel::setGroup(const group<RadioLabel>& group) {
}
void pRadioLabel::setText(string text) {
SetWindowText(hwnd, utf16_t(text));
}
void pRadioLabel::constructor() {
auto pRadioLabel::construct() -> void {
hwnd = CreateWindow(
L"BUTTON", L"",
WS_CHILD | WS_TABSTOP | BS_RADIOBUTTON,
0, 0, 0, 0, parentHwnd, (HMENU)id, GetModuleHandle(0), 0
0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0
);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&radioLabel);
setDefaultFont();
if(radioLabel.state.checked) setChecked();
setText(radioLabel.state.text);
synchronize();
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
pWidget::_setState();
if(state().checked) setChecked();
setText(state().text);
}
void pRadioLabel::destructor() {
auto pRadioLabel::destruct() -> void {
DestroyWindow(hwnd);
}
void pRadioLabel::orphan() {
destructor();
constructor();
auto pRadioLabel::minimumSize() -> Size {
auto size = pFont::size(hfont, state().text);
return {size.width() + 20, size.height() + 4};
}
void pRadioLabel::onActivate() {
if(radioLabel.state.checked) return;
radioLabel.setChecked();
if(radioLabel.onActivate) radioLabel.onActivate();
auto pRadioLabel::setChecked() -> void {
if(auto group = self().group()) {
for(auto& weak : group->state.objects) {
if(auto object = weak.acquire()) {
if(auto radioLabel = dynamic_cast<mRadioLabel*>(object.data())) {
if(auto self = radioLabel->self()) {
SendMessage(self->hwnd, BM_SETCHECK, (WPARAM)(&self->reference == &reference), 0);
}
}
}
}
}
}
auto pRadioLabel::setGroup(sGroup group) -> void {
}
auto pRadioLabel::setText(const string& text) -> void {
SetWindowText(hwnd, utf16_t(text));
}
auto pRadioLabel::onActivate() -> void {
if(state().checked) return;
self().setChecked();
self().doActivate();
}
}
#endif

View File

@@ -0,0 +1,18 @@
#if defined(Hiro_RadioLabel)
namespace hiro {
struct pRadioLabel : pWidget {
Declare(RadioLabel, Widget)
auto minimumSize() -> Size;
auto setChecked() -> void;
auto setGroup(sGroup group) -> void override;
auto setText(const string& text) -> void;
auto onActivate() -> void;
};
}
#endif

View File

@@ -0,0 +1,63 @@
#if defined(Hiro_TabFrame)
namespace hiro {
auto pTabFrameItem::construct() -> void {
}
auto pTabFrameItem::destruct() -> void {
}
auto pTabFrameItem::append(sLayout layout) -> void {
if(auto parent = _parent()) {
parent->_synchronizeLayout();
}
}
auto pTabFrameItem::remove(sLayout layout) -> void {
if(auto parent = _parent()) {
parent->_synchronizeLayout();
}
}
auto pTabFrameItem::setClosable(bool closable) -> void {
//unsupported
}
auto pTabFrameItem::setIcon(const image& icon) -> void {
if(auto parent = _parent()) {
parent->_buildImageList();
}
}
auto pTabFrameItem::setMovable(bool movable) -> void {
//unsupported
}
auto pTabFrameItem::setSelected() -> void {
if(auto parent = _parent()) {
TabCtrl_SetCurSel(parent->hwnd, self().offset());
parent->_synchronizeLayout();
}
}
auto pTabFrameItem::setText(const string& text) -> void {
if(auto parent = _parent()) {
utf16_t wText(text);
TCITEM tcItem;
tcItem.mask = TCIF_TEXT;
tcItem.pszText = (wchar_t*)wText;
TabCtrl_SetItem(parent->hwnd, self().offset(), &tcItem);
}
}
auto pTabFrameItem::_parent() -> maybe<pTabFrame&> {
if(auto parent = self().parentTabFrame()) {
if(auto self = parent->self()) return *self;
}
return nothing;
}
}
#endif

View File

@@ -0,0 +1,21 @@
#if defined(Hiro_TabFrame)
namespace hiro {
struct pTabFrameItem : pObject {
Declare(TabFrameItem, Object)
auto append(sLayout layout) -> void;
auto remove(sLayout layout) -> void;
auto setClosable(bool closable) -> void;
auto setIcon(const image& icon) -> void;
auto setMovable(bool movable) -> void;
auto setSelected() -> void;
auto setText(const string& text) -> void;
auto _parent() -> maybe<pTabFrame&>;
};
}
#endif

View File

@@ -1,128 +1,127 @@
namespace phoenix {
#if defined(Hiro_TabFrame)
static LRESULT CALLBACK TabFrame_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
Object* object = (Object*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if(object == nullptr) return DefWindowProc(hwnd, msg, wparam, lparam);
TabFrame& tabFrame = (TabFrame&)*object;
return Shared_windowProc(tabFrame.p.windowProc, hwnd, msg, wparam, lparam);
}
namespace hiro {
void pTabFrame::append(string text, const image& image) {
unsigned selection = TabCtrl_GetItemCount(hwnd);
wchar_t wtext[] = L"";
TCITEM item;
item.mask = TCIF_TEXT;
item.pszText = wtext;
TabCtrl_InsertItem(hwnd, selection, &item);
setText(selection, text);
if(!image.empty()) setImage(selection, image);
}
void pTabFrame::remove(unsigned selection) {
TabCtrl_DeleteItem(hwnd, selection);
buildImageList();
}
void pTabFrame::setEnabled(bool enabled) {
pWidget::setEnabled(enabled);
for(auto& layout : tabFrame.state.layout) {
if(layout) layout->setEnabled(layout->enabled());
static auto CALLBACK TabFrame_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> LRESULT {
if(auto object = (mObject*)GetWindowLongPtr(hwnd, GWLP_USERDATA)) {
if(auto tabFrame = dynamic_cast<mTabFrame*>(object)) {
return Shared_windowProc(tabFrame->self()->windowProc, hwnd, msg, wparam, lparam);
}
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
void pTabFrame::setGeometry(Geometry geometry) {
pWidget::setGeometry(geometry);
geometry.x += 1, geometry.width -= 4;
geometry.y += 21, geometry.height -= 23;
for(auto& layout : tabFrame.state.layout) {
if(layout) layout->setGeometry(geometry);
}
}
void pTabFrame::setImage(unsigned selection, const image& image) {
buildImageList();
}
void pTabFrame::setSelection(unsigned selection) {
TabCtrl_SetCurSel(hwnd, selection);
synchronizeLayout();
}
void pTabFrame::setText(unsigned selection, string text) {
utf16_t wtext(text);
TCITEM item;
item.mask = TCIF_TEXT;
item.pszText = (wchar_t*)wtext;
TabCtrl_SetItem(hwnd, selection, &item);
}
void pTabFrame::setVisible(bool visible) {
pWidget::setVisible(visible);
for(auto& layout : tabFrame.state.layout) {
if(layout) layout->setVisible(layout->visible());
}
}
void pTabFrame::constructor() {
auto pTabFrame::construct() -> void {
hwnd = CreateWindow(WC_TABCONTROL, L"",
WS_CHILD | WS_TABSTOP,
0, 0, 0, 0, parentHwnd, (HMENU)id, GetModuleHandle(0), 0);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&tabFrame);
0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
windowProc = (WindowProc)GetWindowLongPtr(hwnd, GWLP_WNDPROC);
SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)TabFrame_windowProc);
setDefaultFont();
for(auto& text : tabFrame.state.text) append(text, {});
buildImageList();
setSelection(tabFrame.state.selection);
synchronize();
pWidget::_setState();
for(auto& item : state().items) append(item);
}
void pTabFrame::destructor() {
auto pTabFrame::destruct() -> void {
if(imageList) { ImageList_Destroy(imageList); imageList = nullptr; }
DestroyWindow(hwnd);
}
void pTabFrame::orphan() {
destructor();
constructor();
auto pTabFrame::append(sTabFrameItem item) -> void {
wchar_t text[] = L"";
TCITEM tcItem;
tcItem.mask = TCIF_TEXT;
tcItem.pszText = text;
TabCtrl_InsertItem(hwnd, item->offset(), &tcItem);
if(auto self = item->self()) {
self->setClosable(item->state.closable);
self->setIcon(item->state.icon);
self->setMovable(item->state.movable);
self->setText(item->state.text);
if(item->selected()) self->setSelected();
}
_buildImageList();
_synchronizeLayout();
}
void pTabFrame::buildImageList() {
if(imageList) ImageList_Destroy(imageList);
unsigned size = pFont::size(hfont, " ").height;
auto pTabFrame::remove(sTabFrameItem item) -> void {
TabCtrl_DeleteItem(hwnd, item->offset());
_buildImageList();
}
auto pTabFrame::setEdge(Edge edge) -> void {
//unsupported
}
auto pTabFrame::setEnabled(bool enabled) -> void {
pWidget::setEnabled(enabled);
for(auto& item : state().items) {
if(auto layout = item->state.layout) {
if(auto self = layout->self()) self->setEnabled(layout->enabled(true));
}
}
}
auto pTabFrame::setGeometry(Geometry geometry) -> void {
pWidget::setGeometry(geometry);
geometry.setX(geometry.x() + 1);
geometry.setY(geometry.y() + 21);
geometry.setWidth(geometry.width() - 4);
geometry.setHeight(geometry.height() - 23);
for(auto& item : state().items) {
if(auto layout = item->state.layout) {
layout->setGeometry(geometry);
}
}
}
auto pTabFrame::setVisible(bool visible) -> void {
pWidget::setVisible(visible);
for(auto& item : state().items) {
if(auto layout = item->state.layout) {
if(auto self = layout->self()) self->setVisible(layout->visible(true));
}
}
}
auto pTabFrame::_buildImageList() -> void {
unsigned size = pFont::size(hfont, " ").height();
if(imageList) { ImageList_Destroy(imageList); imageList = nullptr; }
imageList = ImageList_Create(size, size, ILC_COLOR32, 1, 0);
for(auto& image : tabFrame.state.image) {
ImageList_Append(imageList, image, size);
for(auto& item : state().items) {
ImageList_Append(imageList, item->state.icon, size);
}
TabCtrl_SetImageList(hwnd, imageList);
for(unsigned n = 0; n < tabFrame.state.image.size(); n++) {
TCITEM item;
item.mask = TCIF_IMAGE;
item.iImage = (tabFrame.state.image(n).empty() ? -1 : n);
TabCtrl_SetItem(hwnd, n, &item);
for(auto offset : range(state().items)) {
TCITEM tcItem;
tcItem.mask = TCIF_IMAGE;
tcItem.iImage = state().items[offset]->state.icon ? offset : -1;
TabCtrl_SetItem(hwnd, offset, &tcItem);
}
}
void pTabFrame::synchronizeLayout() {
unsigned selection = 0;
for(auto& layout : tabFrame.state.layout) {
if(layout) layout->setVisible(selection == tabFrame.state.selection);
selection++;
auto pTabFrame::_synchronizeLayout() -> void {
for(auto& item : state().items) {
if(auto layout = item->state.layout) {
layout->setVisible(item->selected());
}
}
}
void pTabFrame::onChange() {
tabFrame.state.selection = TabCtrl_GetCurSel(hwnd);
synchronizeLayout();
if(tabFrame.onChange) tabFrame.onChange();
auto pTabFrame::onChange() -> void {
unsigned selected = TabCtrl_GetCurSel(hwnd);
for(auto& item : state().items) item->state.selected = false;
if(auto item = self().item(selected)) item->state.selected = true;
_synchronizeLayout();
self().doChange();
}
//called only if TCS_OWNERDRAWFIXED style is used
//this style disables XP/Vista theming of the TabFrame
void pTabFrame::onDrawItem(LPARAM lparam) {
LPDRAWITEMSTRUCT item = (LPDRAWITEMSTRUCT)lparam;
auto pTabFrame::onDrawItem(LPARAM lparam) -> void {
/*
auto item = (LPDRAWITEMSTRUCT)lparam;
FillRect(item->hDC, &item->rcItem, GetSysColorBrush(COLOR_3DFACE));
SetBkMode(item->hDC, TRANSPARENT);
SetTextColor(item->hDC, GetSysColor(COLOR_BTNTEXT));
@@ -138,6 +137,9 @@ void pTabFrame::onDrawItem(LPARAM lparam) {
}
TextOut(item->hDC, item->rcItem.left + (width - size.width) / 2, item->rcItem.top + 2, utf16_t(text), text.size());
}
*/
}
}
#endif

Some files were not shown because too many files have changed in this diff Show More