Update to v094r43 release.

byuu says:

Updated to compile with all of the new hiro changes. My next step is to
write up hiro API documentation, and move the API from alpha (constantly
changing) to beta (rarely changing), in preparation for the first stable
release (backward-compatible changes only.)

Added "--fullscreen" command-line option. I like this over
a configuration file option. Lets you use the emulator in both modes
without having to modify the config file each time.

Also enhanced the command-line game loading. You can now use any of
these methods:

    higan /path/to/game-folder.sfc
    higan /path/to/game-folder.sfc/
    higan /path/to/game-folder.sfc/program.rom

The idea is to support launchers that insist on loading files only.

Technically, the file can be any name (manifest.bml also works); the
only criteria is that the file actually exists and is a file, and not
a directory. This is a requirement to support the first version (a
directory lacking the trailing / identifier), because I don't want my
nall::string class to query the file system to determine if the string
is an actual existing file or directory for its pathname() / dirname()
functions.

Anyway, every game folder I've made so far has program.rom, and that's
very unlikely to change, so this should be fine.

Now, of course, if you drop a regular "game.sfc" file on the emulator,
it won't even try to load it, unless it's in a folder that ends in .fc,
.sfc, etc. In which case, it'll bail out immediately by being unable to
produce a manifest for what is obviously not really a game folder.
This commit is contained in:
Tim Allen
2015-08-30 12:08:26 +10:00
parent c45633550e
commit 0c87bdabed
230 changed files with 1939 additions and 1408 deletions

View File

@@ -10,7 +10,7 @@ auto pMenuItem::destruct() -> void {
if(hbitmap) { DeleteObject(hbitmap); hbitmap = nullptr; }
}
auto pMenuItem::setIcon(const image& icon) -> void {
auto pMenuItem::setImage(const Image& image) -> void {
_createBitmap();
_synchronize();
}
@@ -26,8 +26,10 @@ auto pMenuItem::onActivate() -> void {
auto pMenuItem::_createBitmap() -> void {
if(hbitmap) { DeleteObject(hbitmap); hbitmap = nullptr; }
if(auto icon = state().icon) {
icon.transform();
if(auto& image = state().image) {
nall::image icon;
icon.allocate(image.width(), image.height());
memory::copy(icon.data(), image.data(), icon.size());
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);

View File

@@ -5,7 +5,7 @@ namespace hiro {
struct pMenuItem : pAction {
Declare(MenuItem, Action)
auto setIcon(const image& icon) -> void;
auto setImage(const Image& image) -> void;
auto setText(const string& text) -> void;
auto onActivate() -> void;

View File

@@ -3,13 +3,14 @@
namespace hiro {
auto pMenuRadioItem::construct() -> void {
setGroup(state().group);
}
auto pMenuRadioItem::destruct() -> void {
}
auto pMenuRadioItem::setChecked() -> void {
if(auto group = self().group()) {
if(auto& group = state().group) {
for(auto& weak : group->state.objects) {
if(auto object = weak.acquire()) {
if(auto menuRadioItem = dynamic_cast<mMenuRadioItem*>(object.data())) {
@@ -32,6 +33,20 @@ auto pMenuRadioItem::setChecked() -> void {
}
auto pMenuRadioItem::setGroup(sGroup group) -> void {
bool first = true;
if(auto& group = state().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()) {
menuRadioItem->state.checked = first;
first = false;
}
}
}
}
}
setChecked();
}
auto pMenuRadioItem::setText(const string& text) -> void {

View File

@@ -19,7 +19,7 @@ auto pMenu::remove(sAction action) -> void {
_synchronize();
}
auto pMenu::setIcon(const image& icon) -> void {
auto pMenu::setImage(const Image& image) -> void {
_createBitmap();
_synchronize();
}
@@ -31,8 +31,10 @@ auto pMenu::setText(const string& text) -> void {
auto pMenu::_createBitmap() -> void {
if(hbitmap) { DeleteObject(hbitmap); hbitmap = 0; }
if(auto icon = state().icon) {
icon.transform();
if(auto& image = state().image) {
nall::image icon;
icon.allocate(image.width(), image.height());
memory::copy(icon.data(), image.data(), icon.size());
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);

View File

@@ -7,7 +7,7 @@ struct pMenu : pAction {
auto append(sAction action) -> void;
auto remove(sAction action) -> void;
auto setIcon(const image& icon) -> void;
auto setImage(const Image& image) -> void;
auto setText(const string& text) -> void;
auto _createBitmap() -> void;

View File

@@ -2,59 +2,14 @@
namespace hiro {
auto pFont::serif(unsigned size, string style) -> string {
if(size == 0) size = 8;
if(style == "") style = "Normal";
return {"Georgia, ", size, ", ", style};
}
auto pFont::sans(unsigned size, string style) -> string {
if(size == 0) size = 8;
if(style == "") style = "Normal";
return {"Tahoma, ", size, ", ", style};
}
auto pFont::monospace(unsigned size, string style) -> string {
if(size == 0) size = 8;
if(style == "") style = "Normal";
return {"Lucida Console, ", size, ", ", style};
}
auto pFont::size(const string& font, const string& text) -> Size {
HFONT hfont = pFont::create(font);
Size size = pFont::size(hfont, text);
auto pFont::size(const Font& font, const string& text) -> Size {
auto hfont = pFont::create(font);
auto size = pFont::size(hfont, text);
pFont::free(hfont);
return size;
}
auto pFont::create(const string& description) -> HFONT {
lstring part = description.split(",", 2L).strip();
string family = "Tahoma";
unsigned size = 8u;
bool bold = false;
bool italic = false;
if(part[0] != "") family = part[0];
if(part.size() >= 2) size = decimal(part[1]);
if(part.size() >= 3) bold = (bool)part[2].find("Bold");
if(part.size() >= 3) italic = (bool)part[2].find("Italic");
return CreateFont(
-(size * 96.0 / 72.0 + 0.5),
0, 0, 0, bold ? FW_BOLD : FW_NORMAL, italic, 0, 0, 0, 0, 0, 0, 0,
utf16_t(family)
);
}
auto pFont::free(HFONT hfont) -> void {
DeleteObject(hfont);
}
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 = " ";
auto pFont::size(HFONT hfont, const string& text) -> Size {
HDC hdc = GetDC(0);
SelectObject(hdc, hfont);
RECT rc = {0, 0, 0, 0};
@@ -63,6 +18,25 @@ auto pFont::size(HFONT hfont, string text) -> Size {
return {rc.right, rc.bottom};
}
auto pFont::family(const string& family) -> string {
if(family == Font::Sans ) return "Tahoma";
if(family == Font::Serif) return "Georgia";
if(family == Font::Mono ) return "Lucida Console";
return family ? family : "Tahoma";
}
auto pFont::create(const Font& font) -> HFONT {
return CreateFont(
-((font.size() ? font.size() : 8) * 96.0 / 72.0 + 0.5),
0, 0, 0, font.bold() ? FW_BOLD : FW_NORMAL, font.italic(), 0, 0, 0, 0, 0, 0, 0,
utf16_t(family(font.family()))
);
}
auto pFont::free(HFONT hfont) -> void {
DeleteObject(hfont);
}
}
#endif

View File

@@ -3,14 +3,11 @@
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 size(const Font& font, const string& text) -> Size;
static auto size(HFONT hfont, const string& text) -> Size;
static auto family(const string& family) -> string;
static auto create(const Font& font) -> HFONT;
static auto free(HFONT hfont) -> void;
static auto size(HFONT hfont, string text) -> Size;
};
}

View File

@@ -35,6 +35,30 @@
#define Button_SetImageList(hwnd, pbuttonImagelist) (WINBOOL)SNDMSG((hwnd),BCM_SETIMAGELIST,0,(LPARAM)(pbuttonImagelist))
#endif
#if !defined(BP_PUSHBUTTON)
#define BP_PUSHBUTTON 1
#endif
#if !defined(PBS_NORMAL)
#define PBS_NORMAL 1
#endif
#if !defined(PBS_HOT)
#define PBS_HOT 2
#endif
#if !defined(PBS_PRESSED)
#define PBS_PRESSED 3
#endif
#if !defined(PBS_DISABLED)
#define PBS_DISABLED 4
#endif
#if !defined(PBS_DEFAULTED)
#define PBS_DEFAULTED 5
#endif
#if !defined(BP_CHECKBOX)
#define BP_CHECKBOX 3
#endif

View File

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

View File

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

View File

@@ -14,7 +14,7 @@ auto pLayout::setEnabled(bool enabled) -> void {
}
}
auto pLayout::setFont(const string& font) -> void {
auto pLayout::setFont(const Font& font) -> void {
for(auto& sizable : state().sizables) {
if(auto self = sizable->self()) self->setFont(sizable->font(true));
}

View File

@@ -6,7 +6,7 @@ struct pLayout : pSizable {
Declare(Layout, Sizable)
auto setEnabled(bool enabled) -> void override;
auto setFont(const string& font) -> void override;
auto setFont(const Font& font) -> void override;
auto setVisible(bool visible) -> void override;
};

View File

@@ -25,7 +25,7 @@ auto pMenuBar::setEnabled(bool enabled) -> void {
_update();
}
auto pMenuBar::setFont(const string& font) -> void {
auto pMenuBar::setFont(const Font& font) -> void {
//unsupported
}

View File

@@ -8,7 +8,7 @@ struct pMenuBar : pObject {
auto append(sMenu menu) -> void;
auto remove(sMenu menu) -> void;
auto setEnabled(bool enabled) -> void override;
auto setFont(const string& font) -> void override;
auto setFont(const Font& font) -> void override;
auto setVisible(bool visible) -> void override;
auto _parent() -> maybe<pWindow&>;

View File

@@ -27,7 +27,7 @@ auto pObject::setEnabled(bool enabled) -> void {
auto pObject::setFocused() -> void {
}
auto pObject::setFont(const string& font) -> void {
auto pObject::setFont(const Font& font) -> void {
}
auto pObject::setGroup(sGroup group) -> void {

View File

@@ -14,7 +14,7 @@ struct pObject {
virtual auto reset() -> void;
virtual auto setEnabled(bool enabled) -> void;
virtual auto setFocused() -> void;
virtual auto setFont(const string& font) -> void;
virtual auto setFont(const Font& font) -> void;
virtual auto setGroup(sGroup group) -> void;
virtual auto setVisible(bool visible) -> void;

View File

@@ -11,7 +11,6 @@
#include "object.cpp"
#include "group.cpp"
#include "hotkey.cpp"
#include "font.cpp"
#include "timer.cpp"
#include "window.cpp"

View File

@@ -41,7 +41,6 @@ static vector<wObject> windows;
#include "object.hpp"
#include "group.hpp"
#include "hotkey.hpp"
#include "timer.hpp"
#include "window.hpp"
#include "status-bar.hpp"

View File

@@ -17,7 +17,7 @@ auto pPopupMenu::append(sAction action) -> void {
auto pPopupMenu::remove(sAction action) -> void {
}
auto pPopupMenu::setFont(const string& font) -> void {
auto pPopupMenu::setFont(const Font& font) -> void {
}
auto pPopupMenu::setVisible(bool visible) -> void {

View File

@@ -7,7 +7,7 @@ struct pPopupMenu : pObject {
auto append(sAction action) -> void;
auto remove(sAction action) -> void;
auto setFont(const string& font) -> void override;
auto setFont(const Font& font) -> void override;
auto setVisible(bool visible) -> void override;
HWND hwnd = nullptr;

View File

@@ -25,7 +25,7 @@ auto pStatusBar::setEnabled(bool enabled) -> void {
//unsupported
}
auto pStatusBar::setFont(const string& font) -> void {
auto pStatusBar::setFont(const Font& font) -> void {
if(hfont) DeleteObject(hfont);
hfont = pFont::create(font);
if(hwnd) SendMessage(hwnd, WM_SETFONT, (WPARAM)hfont, 0);

View File

@@ -6,7 +6,7 @@ struct pStatusBar : pObject {
Declare(StatusBar, Object)
auto setEnabled(bool enabled) -> void override;
auto setFont(const string& font) -> void override;
auto setFont(const Font& font) -> void override;
auto setText(const string& text) -> void;
auto setVisible(bool visible) -> void override;

View File

@@ -5,6 +5,8 @@ static const unsigned WindowsXP = 0x0501;
static const unsigned WindowsVista = 0x0600;
static const unsigned Windows7 = 0x0601;
static auto Button_CustomDraw(HWND, PAINTSTRUCT&, unsigned, const Font&, const Image&, Orientation, const string&) -> void;
static auto OsVersion() -> unsigned {
OSVERSIONINFO versionInfo{0};
versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
@@ -12,7 +14,9 @@ static auto OsVersion() -> unsigned {
return (versionInfo.dwMajorVersion << 8) + (versionInfo.dwMajorVersion << 0);
}
static auto CreateBitmap(const image& icon) -> HBITMAP {
static auto CreateBitmap(image icon) -> HBITMAP {
icon.alphaMultiply(); //Windows AlphaBlend() requires premultiplied image data
icon.transform();
HDC hdc = GetDC(0);
BITMAPINFO bitmapInfo;
memset(&bitmapInfo, 0, sizeof(BITMAPINFO));
@@ -30,6 +34,33 @@ static auto CreateBitmap(const image& icon) -> HBITMAP {
return hbitmap;
}
static auto CreateBitmap(const Image& image) -> HBITMAP {
HDC hdc = GetDC(0);
BITMAPINFO bitmapInfo{0};
bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bitmapInfo.bmiHeader.biWidth = image.width();
bitmapInfo.bmiHeader.biHeight = -image.height(); //bitmaps are stored upside down unless we negate height
bitmapInfo.bmiHeader.biPlanes = 1;
bitmapInfo.bmiHeader.biBitCount = 32;
bitmapInfo.bmiHeader.biCompression = BI_RGB;
bitmapInfo.bmiHeader.biSizeImage = image.width() * image.height() * sizeof(uint32_t);
void* bits = nullptr;
HBITMAP hbitmap = CreateDIBSection(hdc, &bitmapInfo, DIB_RGB_COLORS, &bits, nullptr, 0);
if(bits) {
auto source = (const uint8_t*)image.data();
auto target = (uint8_t*)bits;
for(auto n : range(image.width() * image.height())) {
target[0] = (source[0] * source[3]) / 255;
target[1] = (source[1] * source[3]) / 255;
target[2] = (source[2] * source[3]) / 255;
target[3] = (source[3]);
source += 4, target += 4;
}
}
ReleaseDC(0, hdc);
return hbitmap;
}
static auto CreateRGB(const Color& color) -> COLORREF {
return RGB(color.red(), color.green(), color.blue());
}
@@ -62,16 +93,18 @@ static auto GetWindowZOrder(HWND hwnd) -> unsigned {
return z;
}
static auto ImageList_Append(HIMAGELIST imageList, const image& source, unsigned scale) -> void {
auto image = source;
if(image.empty()) {
image.allocate(scale, scale);
image.fill(GetSysColor(COLOR_WINDOW));
static auto ImageList_Append(HIMAGELIST imageList, const Image& image, unsigned scale) -> void {
nall::image icon;
if(image) {
icon.allocate(image.width(), image.height());
memory::copy(icon.data(), image.data(), icon.size());
icon.scale(scale, scale);
} else {
icon.allocate(scale, scale);
icon.fill(GetSysColor(COLOR_WINDOW));
}
image.transform();
image.scale(scale, scale);
HBITMAP bitmap = CreateBitmap(image);
ImageList_Add(imageList, bitmap, NULL);
HBITMAP bitmap = CreateBitmap(icon);
ImageList_Add(imageList, bitmap, nullptr);
DeleteObject(bitmap);
}

View File

@@ -2,44 +2,92 @@
namespace hiro {
static auto Button_paintProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam,
bool bordered, bool checked, bool enabled, const Font& font, const Image& image, Orientation orientation, const string& text
) -> LRESULT {
if(msg == WM_PAINT) {
PAINTSTRUCT ps;
BeginPaint(hwnd, &ps);
auto state = Button_GetState(hwnd);
Button_CustomDraw(hwnd, ps,
(state & BST_PUSHED || checked) ? PBS_PRESSED
: (state & BST_HOT) ? PBS_HOT
: bordered ? (enabled ? PBS_NORMAL : PBS_DISABLED)
: 0, font, image, orientation, text
);
EndPaint(hwnd, &ps);
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
//BUTTON cannot draw borderless buttons on its own
//BS_OWNERDRAW will send WM_DRAWITEM; but will disable hot-tracking notifications
//to gain hot-tracking plus borderless buttons; BUTTON is superclassed and WM_PAINT is hijacked
//note: letting hiro paint bordered buttons will lose the fade animations on Vista+;
//however, it will allow placing icons immediately next to text (original forces icon left alignment)
static auto CALLBACK Button_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> LRESULT {
if(auto object = (mObject*)GetWindowLongPtr(hwnd, GWLP_USERDATA)) {
if(auto button = dynamic_cast<mButton*>(object)) {
if(auto self = button->self()) {
if(msg == WM_ERASEBKGND) return DefWindowProc(hwnd, msg, wparam, lparam);
if(msg == WM_PAINT) return Button_paintProc(hwnd, msg, wparam, lparam,
button->state.bordered, false, button->enabled(true), button->font(true),
button->state.image, button->state.orientation, button->state.text
);
return self->windowProc(hwnd, msg, wparam, lparam);
}
}
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
auto pButton::construct() -> void {
hwnd = CreateWindow(
L"BUTTON", L"",
WS_CHILD | WS_TABSTOP,
L"BUTTON", L"", WS_CHILD | WS_TABSTOP,
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)Button_windowProc);
pWidget::_setState();
_setState();
setBordered(state().bordered);
}
auto pButton::destruct() -> void {
if(hbitmap) { DeleteObject(hbitmap); hbitmap = 0; }
if(himagelist) { ImageList_Destroy(himagelist); himagelist = 0; }
DestroyWindow(hwnd);
}
auto pButton::minimumSize() const -> Size {
auto size = pFont::size(hfont, state().text);
Size image = state().image.size();
Size text = state().text ? pFont::size(self().font(true), state().text) : Size{};
Size size;
if(state().orientation == Orientation::Horizontal) {
size.setWidth(size.width() + state().icon.width());
size.setHeight(max(size.height(), state().icon.height()));
size.setWidth(image.width() + (image && text ? 5 : 0) + text.width());
size.setHeight(max(image.height(), text.height()));
}
if(state().orientation == Orientation::Vertical) {
size.setWidth(max(size.width(), state().icon.width()));
size.setHeight(size.height() + state().icon.height());
size.setWidth(max(image.width(), text.width()));
size.setHeight(image.height() + (image && text ? 5 : 0) + text.height());
}
return {size.width() + (state().text ? 20 : 13), size.height() + 10};
size.setHeight(max(size.height(), pFont::size(self().font(true), " ").height()));
return {size.width() + (state().bordered && text ? 20 : 10), size.height() + 10};
}
auto pButton::setBordered(bool bordered) -> void {
_setState();
}
auto pButton::setIcon(const image& icon) -> void {
auto pButton::setEnabled(bool enabled) -> void {
pWidget::setEnabled(enabled);
_setState();
}
auto pButton::setFont(const Font& font) -> void {
pWidget::setFont(font);
_setState();
}
auto pButton::setImage(const Image& image) -> void {
_setState();
}
@@ -51,42 +99,84 @@ auto pButton::setText(const string& text) -> void {
_setState();
}
auto pButton::setVisible(bool visible) -> void {
pWidget::setVisible(visible);
_setState();
}
auto pButton::onActivate() -> void {
self().doActivate();
}
//performs setIcon, setOrientation, setText
auto pButton::_setState() -> void {
image icon = state().icon;
icon.transform();
InvalidateRect(hwnd, 0, false);
}
if(hbitmap) { DeleteObject(hbitmap); hbitmap = 0; }
if(himagelist) { ImageList_Destroy(himagelist); himagelist = 0; }
//this function is designed to be used with Button, CheckButton, and RadioButton
auto Button_CustomDraw(HWND hwnd, PAINTSTRUCT& ps, unsigned state, const Font& font, const Image& image, Orientation orientation, const string& text) -> void {
RECT rc;
GetClientRect(hwnd, &rc);
Geometry geometry{rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top}, imageGeometry, textGeometry;
if(image) imageGeometry.setSize(image.size());
if(text) textGeometry.setSize(pFont::size(font, text));
//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) icon.alphaBlend(GetSysColor(COLOR_BTNFACE));
Position position;
Size size;
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;
switch(orientation) {
case Orientation::Horizontal:
size = {imageGeometry.width() + (image && text ? 5 : 0) + textGeometry.width(), max(imageGeometry.height(), textGeometry.height())};
position = {(geometry.width() - size.width()) / 2, (geometry.height() - size.height()) / 2};
imageGeometry.setPosition({position.x(), position.y() + (size.height() - imageGeometry.height()) / 2});
textGeometry.setPosition({position.x() + size.width() - textGeometry.width(), position.y() + (size.height() - textGeometry.height()) / 2});
break;
case Orientation::Vertical:
size = {max(imageGeometry.width(), textGeometry.width()), imageGeometry.height() + (image && text ? 5 : 0) + textGeometry.height()};
position = {(geometry.width() - size.width()) / 2, (geometry.height() - size.height()) / 2};
imageGeometry.setPosition({position.x() + (size.width() - imageGeometry.width()) / 2, position.y()});
textGeometry.setPosition({position.x() + (size.width() - textGeometry.width()) / 2, position.y() + size.height() - textGeometry.height()});
break;
}
Button_SetImageList(hwnd, &list);
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);
SetWindowText(hwnd, L"");
HDC hdcSource = CreateCompatibleDC(ps.hdc);
DrawThemeParentBackground(hwnd, ps.hdc, &rc);
if(state) {
if(auto theme = OpenThemeData(hwnd, L"BUTTON")) {
DrawThemeBackground(theme, ps.hdc, BP_PUSHBUTTON, state, &rc, &ps.rcPaint);
CloseThemeData(theme);
}
}
if(GetFocus() == hwnd) {
signed offset = state ? 4 : 1;
RECT rcFocus{rc.left + offset, rc.top + offset, rc.right - offset, rc.bottom - offset};
if(!state || state == PBS_NORMAL) DrawFocusRect(ps.hdc, &rcFocus);
}
if(image) {
auto bitmap = CreateBitmap(image);
SelectBitmap(hdcSource, bitmap);
BLENDFUNCTION blend{AC_SRC_OVER, 0, (BYTE)(IsWindowEnabled(hwnd) ? 255 : 128), AC_SRC_ALPHA};
AlphaBlend(
ps.hdc, imageGeometry.x(), imageGeometry.y(), image.width(), image.height(),
hdcSource, 0, 0, image.width(), image.height(), blend
);
DeleteObject(bitmap);
}
if(text) {
utf16_t wText(text);
SetBkMode(ps.hdc, TRANSPARENT);
SetTextColor(ps.hdc, GetSysColor(IsWindowEnabled(hwnd) ? COLOR_BTNTEXT : COLOR_GRAYTEXT));
auto hFont = pFont::create(font);
SelectObject(ps.hdc, hFont);
RECT rcText{textGeometry.x(), textGeometry.y(), textGeometry.x() + textGeometry.width(), textGeometry.y() + textGeometry.height()};
DrawText(ps.hdc, wText, -1, &rcText, DT_NOPREFIX | DT_END_ELLIPSIS);
DeleteObject(hFont);
}
DeleteDC(hdcSource);
}
}

View File

@@ -7,16 +7,18 @@ struct pButton : pWidget {
auto minimumSize() const -> Size override;
auto setBordered(bool bordered) -> void;
auto setIcon(const image& icon) -> void;
auto setEnabled(bool enabled) -> void override;
auto setFont(const Font& font) -> void override;
auto setImage(const Image& image) -> void;
auto setOrientation(Orientation orientation) -> void;
auto setText(const string& text) -> void;
auto setVisible(bool visible) -> void override;
auto onActivate() -> void;
auto _setState() -> void;
HBITMAP hbitmap;
HIMAGELIST himagelist;
WindowProc windowProc = nullptr;
};
}

View File

@@ -69,16 +69,11 @@ auto pCanvas::destruct() -> void {
}
auto pCanvas::minimumSize() const -> Size {
return {max(0, state().size.width()), max(0, state().size.height())};
if(auto& image = state().image) return image.size();
return {0, 0};
}
auto pCanvas::setColor(Color color) -> void {
mode = Mode::Color;
update();
}
auto pCanvas::setData(Size size) -> void {
mode = Mode::Data;
update();
}
@@ -91,13 +86,11 @@ auto pCanvas::setGeometry(Geometry geometry) -> void {
update();
}
auto pCanvas::setGradient(Color topLeft, Color topRight, Color bottomLeft, Color bottomRight) -> void {
mode = Mode::Gradient;
auto pCanvas::setGradient(Gradient gradient) -> void {
update();
}
auto pCanvas::setIcon(const image& icon) -> void {
mode = Mode::Icon;
auto pCanvas::setImage(const Image& image) -> void {
update();
}
@@ -121,8 +114,18 @@ auto pCanvas::_paint() -> void {
bmi.bmiHeader.biHeight = -height; //GDI stores bitmaps upside now; negative height flips bitmap
bmi.bmiHeader.biSizeImage = pixels.size() * sizeof(uint32_t);
void* bits = nullptr;
HBITMAP bitmap = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, &bits, NULL, 0);
if(bits) memory::copy(bits, pixels.data(), pixels.size() * sizeof(uint32_t));
HBITMAP bitmap = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, &bits, nullptr, 0);
if(bits) {
auto source = (const uint8_t*)pixels.data();
auto target = (uint8_t*)bits;
for(auto n : range(width * height)) {
target[0] = (source[0] * source[3]) / 255;
target[1] = (source[1] * source[3]) / 255;
target[2] = (source[2] * source[3]) / 255;
target[3] = (source[3]);
source += 4, target += 4;
}
}
SelectObject(hdc, bitmap);
RECT rc;
@@ -139,45 +142,29 @@ auto pCanvas::_paint() -> void {
}
auto pCanvas::_rasterize() -> void {
if(mode == Mode::Color || mode == Mode::Gradient) {
if(auto& image = state().image) {
width = image.width();
height = image.height();
} else {
width = self().geometry().width();
height = self().geometry().height();
} else {
width = state().size.width();
height = state().size.height();
}
if(width <= 0 || height <= 0) return;
pixels.reallocate(width * height);
if(mode == Mode::Color) {
if(auto& image = state().image) {
memory::copy(pixels.data(), image.data(), width * height * sizeof(uint32_t));
} else if(auto& gradient = state().gradient) {
auto& colors = gradient.state.colors;
nall::image fill;
fill.allocate(width, height);
fill.gradient(colors[0].value(), colors[1].value(), colors[2].value(), colors[3].value());
memory::copy(pixels.data(), fill.data(), fill.size());
} else {
uint32_t color = state().color.value();
for(auto& pixel : pixels) pixel = color;
}
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()
);
memory::copy(pixels.data(), fill.data(), fill.size());
}
if(mode == Mode::Icon) {
auto icon = state().icon;
icon.scale(width, height);
icon.transform();
memory::copy(pixels.data(), icon.data(), icon.size());
}
if(mode == Mode::Data) {
memory::copy(
pixels.data(), pixels.size() * sizeof(uint32_t),
state().data.data(), state().data.size() * sizeof(uint32_t)
);
}
}
auto pCanvas::_redraw() -> void {

View File

@@ -7,20 +7,16 @@ struct pCanvas : pWidget {
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 setGradient(Gradient gradient) -> void;
auto setImage(const Image& image) -> 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;

View File

@@ -2,47 +2,73 @@
namespace hiro {
static auto CALLBACK CheckButton_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> LRESULT {
if(auto object = (mObject*)GetWindowLongPtr(hwnd, GWLP_USERDATA)) {
if(auto button = dynamic_cast<mCheckButton*>(object)) {
if(auto self = button->self()) {
if(msg == WM_ERASEBKGND) return DefWindowProc(hwnd, msg, wparam, lparam);
if(msg == WM_PAINT) return Button_paintProc(hwnd, msg, wparam, lparam,
button->state.bordered, button->state.checked, button->enabled(true), button->font(true),
button->state.image, button->state.orientation, button->state.text
);
return self->windowProc(hwnd, msg, wparam, lparam);
}
}
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
auto pCheckButton::construct() -> void {
hwnd = CreateWindow(L"BUTTON", L"",
WS_CHILD | WS_TABSTOP | BS_CHECKBOX | BS_PUSHLIKE,
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)CheckButton_windowProc);
pWidget::_setState();
_setState();
setBordered(state().bordered);
setChecked(state().checked);
}
auto pCheckButton::destruct() -> void {
if(hbitmap) { DeleteObject(hbitmap); hbitmap = 0; }
if(himagelist) { ImageList_Destroy(himagelist); himagelist = 0; }
DestroyWindow(hwnd);
}
auto pCheckButton::minimumSize() const -> Size {
auto size = pFont::size(hfont, state().text);
Size image = state().image.size();
Size text = state().text ? pFont::size(self().font(true), state().text) : Size{};
Size size;
if(state().orientation == Orientation::Horizontal) {
size.setWidth(size.width() + state().icon.width());
size.setHeight(max(size.height(), state().icon.height()));
size.setWidth(image.width() + (image && text ? 5 : 0) + text.width());
size.setHeight(max(image.height(), text.height()));
}
if(state().orientation == Orientation::Vertical) {
size.setWidth(max(size.width(), state().icon.width()));
size.setHeight(size.height() + state().icon.height());
size.setWidth(max(image.width(), text.width()));
size.setHeight(image.height() + (image && text ? 5 : 0) + text.height());
}
return {size.width() + (state().text ? 20 : 10), size.height() + 10};
size.setHeight(max(size.height(), pFont::size(self().font(true), " ").height()));
return {size.width() + (state().bordered && text ? 20 : 10), size.height() + 10};
}
auto pCheckButton::setBordered(bool bordered) -> void {
_setState();
}
auto pCheckButton::setChecked(bool checked) -> void {
SendMessage(hwnd, BM_SETCHECK, (WPARAM)checked, 0);
}
auto pCheckButton::setIcon(const image& icon) -> void {
auto pCheckButton::setEnabled(bool enabled) -> void {
pWidget::setEnabled(enabled);
_setState();
}
auto pCheckButton::setFont(const Font& font) -> void {
pWidget::setFont(font);
_setState();
}
auto pCheckButton::setImage(const Image& image) -> void {
_setState();
}
@@ -54,6 +80,11 @@ auto pCheckButton::setText(const string& text) -> void {
_setState();
}
auto pCheckButton::setVisible(bool visible) -> void {
pWidget::setVisible(visible);
_setState();
}
auto pCheckButton::onToggle() -> void {
state().checked = !state().checked;
setChecked(state().checked);
@@ -61,32 +92,7 @@ auto pCheckButton::onToggle() -> void {
}
auto pCheckButton::_setState() -> void {
image icon = state().icon;
icon.transform();
if(hbitmap) { DeleteObject(hbitmap); hbitmap = nullptr; }
if(himagelist) { ImageList_Destroy(himagelist); himagelist = nullptr; }
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, nullptr);
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"");
}
InvalidateRect(hwnd, 0, false);
}
}

View File

@@ -7,17 +7,19 @@ struct pCheckButton : pWidget {
auto minimumSize() const -> Size override;
auto setBordered(bool bordered) -> void;
auto setEnabled(bool enabled) -> void override;
auto setFont(const Font& font) -> void override;
auto setChecked(bool checked) -> void;
auto setIcon(const image& icon) -> void;
auto setImage(const Image& image) -> void;
auto setOrientation(Orientation orientation) -> void;
auto setText(const string& text) -> void;
auto setVisible(bool visible) -> void override;
auto onToggle() -> void;
auto _setState() -> void;
HBITMAP hbitmap = 0;
HIMAGELIST himagelist = 0;
WindowProc windowProc = nullptr;
};
}

View File

@@ -19,7 +19,7 @@ auto pCheckLabel::destruct() -> void {
}
auto pCheckLabel::minimumSize() const -> Size {
auto size = pFont::size(hfont, state().text);
auto size = pFont::size(self().font(true), state().text ? state().text : " ");
return {size.width() + 20, size.height() + 4};
}

View File

@@ -8,7 +8,7 @@ auto pComboButtonItem::construct() -> void {
auto pComboButtonItem::destruct() -> void {
}
auto pComboButtonItem::setIcon(const image& icon) -> void {
auto pComboButtonItem::setImage(const Image& image) -> void {
//unsupported
}

View File

@@ -5,7 +5,7 @@ namespace hiro {
struct pComboButtonItem : pObject {
Declare(ComboButtonItem, Object)
auto setIcon(const image& icon) -> void;
auto setImage(const Image& image) -> void;
auto setSelected() -> void;
auto setText(const string& text) -> void;

View File

@@ -21,7 +21,7 @@ auto pLineEdit::destruct() -> void {
}
auto pLineEdit::minimumSize() const -> Size {
auto size = pFont::size(hfont, state().text);
auto size = pFont::size(hfont, state().text ? state().text : " ");
return {size.width() + 12, size.height() + 10};
}

View File

@@ -27,7 +27,7 @@ auto pListViewCell::setForegroundColor(Color color) -> void {
_repaint();
}
auto pListViewCell::setIcon(const image& icon) -> void {
auto pListViewCell::setImage(const Image& icon) -> void {
_repaint();
}

View File

@@ -10,7 +10,7 @@ struct pListViewCell : pObject {
auto setCheckable(bool checkable) -> void;
auto setChecked(bool checked) -> void;
auto setForegroundColor(Color color) -> void;
auto setIcon(const image& icon) -> void;
auto setImage(const Image& image) -> void;
auto setText(const string& text) -> void;
auto _parent() -> maybe<pListViewItem&>;

View File

@@ -49,7 +49,7 @@ auto pListViewColumn::setHorizontalAlignment(double alignment) -> void {
_setState();
}
auto pListViewColumn::setIcon(const image& icon) -> void {
auto pListViewColumn::setImage(const Image& image) -> void {
_setState();
}
@@ -97,7 +97,7 @@ auto pListViewColumn::_setState() -> void {
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().image) lvColumn.mask |= LVCF_IMAGE;
if(!state().resizable) lvColumn.fmt |= LVCFMT_FIXED_WIDTH;
ListView_SetColumn(grandparent->hwnd, self().offset(), &lvColumn);
grandparent->unlock();

View File

@@ -12,7 +12,7 @@ struct pListViewColumn : pObject {
auto setExpandable(bool expandable) -> void;
auto setForegroundColor(Color color) -> void;
auto setHorizontalAlignment(double alignment) -> void;
auto setIcon(const image& icon) -> void;
auto setImage(const Image& image) -> void;
auto setResizable(bool resizable) -> void;
auto setSortable(bool sortable) -> void;
auto setText(const string& text) -> void;

View File

@@ -219,13 +219,11 @@ auto pListView::onCustomDraw(LPARAM lparam) -> LRESULT {
rc.left += 2;
}
if(auto icon = cell->state.icon) {
icon.scale(iconSize, iconSize);
icon.transform();
auto bitmap = CreateBitmap(icon);
if(auto& image = cell->state.image) {
auto bitmap = CreateBitmap(image);
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);
AlphaBlend(hdc, rc.left, rc.top, iconSize, iconSize, hdcSource, 0, 0, image.width(), image.height(), blend);
DeleteObject(bitmap);
rc.left += iconSize + 2;
}
@@ -325,11 +323,11 @@ auto pListView::_cellWidth(unsigned _row, unsigned _column) -> unsigned {
if(cell->state.checkable) {
width += 16 + 2;
}
if(auto& icon = cell->state.icon) {
if(auto& image = cell->state.image) {
width += 16 + 2;
}
if(auto& text = cell->state.text) {
width += Font::size(_font(_row, _column), text).width();
width += pFont::size(_font(_row, _column), text).width();
}
}
}
@@ -340,18 +338,18 @@ auto pListView::_columnWidth(unsigned _column) -> unsigned {
unsigned width = 12;
if(auto header = state().header) {
if(auto column = header->column(_column)) {
if(auto& icon = column->state.icon) {
if(auto& image = column->state.image) {
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();
width += pFont::size(self().font(true), text).width();
}
}
}
return width;
}
auto pListView::_font(unsigned _row, unsigned _column) -> string {
auto pListView::_font(unsigned _row, unsigned _column) -> Font {
if(auto item = self().item(_row)) {
if(auto cell = item->cell(_column)) {
if(auto font = cell->font()) return font;
@@ -362,7 +360,7 @@ auto pListView::_font(unsigned _row, unsigned _column) -> string {
// if(auto font = column->font()) return font;
// }
if(auto font = self().font(true)) return font;
return Font::sans(8);
return {};
}
auto pListView::_foregroundColor(unsigned _row, unsigned _column) -> Color {
@@ -387,10 +385,11 @@ auto pListView::_setIcons() -> void {
if(auto& header = state().header) {
for(auto column : range(header->columnCount())) {
auto icon = header->state.columns[column]->state.icon;
if(icon) {
nall::image icon;
if(auto& image = header->state.columns[column]->state.image) {
icon.allocate(image.width(), image.height());
memory::copy(icon.data(), image.data(), icon.size());
icon.scale(16, 16);
icon.transform();
} else {
icon.allocate(16, 16);
icon.fill(0x00ffffff);
@@ -402,7 +401,7 @@ auto pListView::_setIcons() -> void {
}
//empty icon used for ListViewItems (drawn manually via onCustomDraw)
image icon;
nall::image icon;
icon.allocate(16, 16);
icon.fill(0x00ffffff);
auto bitmap = CreateBitmap(icon);

View File

@@ -27,7 +27,7 @@ struct pListView : pWidget {
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 _font(unsigned row, unsigned column) -> Font;
auto _foregroundColor(unsigned row, unsigned column) -> Color;
auto _setIcons() -> void;
auto _setSortable() -> void;

View File

@@ -2,44 +2,60 @@
namespace hiro {
static auto CALLBACK RadioButton_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> LRESULT {
if(auto object = (mObject*)GetWindowLongPtr(hwnd, GWLP_USERDATA)) {
if(auto button = dynamic_cast<mRadioButton*>(object)) {
if(auto self = button->self()) {
if(msg == WM_ERASEBKGND) return DefWindowProc(hwnd, msg, wparam, lparam);
if(msg == WM_PAINT) return Button_paintProc(hwnd, msg, wparam, lparam,
button->state.bordered, button->state.checked, button->enabled(true), button->font(true),
button->state.image, button->state.orientation, button->state.text
);
return self->windowProc(hwnd, msg, wparam, lparam);
}
}
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
auto pRadioButton::construct() -> void {
hwnd = CreateWindow(L"BUTTON", L"",
WS_CHILD | WS_TABSTOP | BS_CHECKBOX | BS_PUSHLIKE,
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)RadioButton_windowProc);
pWidget::_setState();
setGroup(state().group);
_setState();
setBordered(state().bordered);
if(state().checked) setChecked();
}
auto pRadioButton::destruct() -> void {
if(hbitmap) { DeleteObject(hbitmap); hbitmap = 0; }
if(himagelist) { ImageList_Destroy(himagelist); himagelist = 0; }
DestroyWindow(hwnd);
}
auto pRadioButton::minimumSize() const -> Size {
auto size = pFont::size(hfont, state().text);
Size image = state().image.size();
Size text = state().text ? pFont::size(self().font(true), state().text) : Size{};
Size size;
if(state().orientation == Orientation::Horizontal) {
size.setWidth(size.width() + state().icon.width());
size.setHeight(max(size.height(), state().icon.height()));
size.setWidth(image.width() + (image && text ? 5 : 0) + text.width());
size.setHeight(max(image.height(), text.height()));
}
if(state().orientation == Orientation::Vertical) {
size.setWidth(max(size.width(), state().icon.width()));
size.setHeight(size.height() + state().icon.height());
size.setWidth(max(image.width(), text.width()));
size.setHeight(image.height() + (image && text ? 5 : 0) + text.height());
}
return {size.width() + (state().text ? 20 : 10), size.height() + 10};
size.setHeight(max(size.height(), pFont::size(self().font(true), " ").height()));
return {size.width() + (state().bordered && text ? 20 : 10), size.height() + 10};
}
auto pRadioButton::setBordered(bool bordered) -> void {
_setState();
}
auto pRadioButton::setChecked() -> void {
if(auto group = self().group()) {
if(auto& group = state().group) {
for(auto& weak : group->state.objects) {
if(auto object = weak.acquire()) {
if(auto radioButton = dynamic_cast<mRadioButton*>(object.data())) {
@@ -52,10 +68,33 @@ auto pRadioButton::setChecked() -> void {
}
}
auto pRadioButton::setGroup(sGroup group) -> void {
auto pRadioButton::setEnabled(bool enabled) -> void {
pWidget::setEnabled(enabled);
_setState();
}
auto pRadioButton::setIcon(const image& icon) -> void {
auto pRadioButton::setFont(const Font& font) -> void {
pWidget::setFont(font);
_setState();
}
auto pRadioButton::setGroup(sGroup group) -> void {
bool first = true;
if(auto& group = state().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)(radioButton->state.checked = first), 0);
first = false;
}
}
}
}
}
}
auto pRadioButton::setImage(const Image& image) -> void {
_setState();
}
@@ -63,43 +102,23 @@ auto pRadioButton::setOrientation(Orientation orientation) -> void {
_setState();
}
void pRadioButton::setText(const string& text) {
auto pRadioButton::setText(const string& text) -> void {
_setState();
}
void pRadioButton::onActivate() {
auto pRadioButton::setVisible(bool visible) -> void {
pWidget::setVisible(visible);
_setState();
}
auto pRadioButton::onActivate() -> void {
if(state().checked) return;
self().setChecked();
self().doActivate();
}
auto pRadioButton::_setState() -> void {
image icon = state().icon;
icon.transform();
if(hbitmap) { DeleteObject(hbitmap); hbitmap = nullptr; }
if(himagelist) { ImageList_Destroy(himagelist); himagelist = nullptr; }
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, nullptr);
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"");
}
InvalidateRect(hwnd, 0, false);
}
}

View File

@@ -8,17 +8,19 @@ struct pRadioButton : pWidget {
auto minimumSize() const -> Size override;
auto setBordered(bool bordered) -> void;
auto setChecked() -> void;
auto setEnabled(bool enabled) -> void override;
auto setFont(const Font& font) -> void override;
auto setGroup(sGroup group) -> void override;
auto setIcon(const image& icon) -> void;
auto setImage(const Image& image) -> void;
auto setOrientation(Orientation orientation) -> void;
auto setText(const string& text) -> void;
auto setVisible(bool visible) -> void override;
auto onActivate() -> void;
auto _setState() -> void;
HBITMAP hbitmap = 0;
HIMAGELIST himagelist = 0;
WindowProc windowProc = nullptr;
};
}

View File

@@ -4,13 +4,12 @@ namespace hiro {
auto pRadioLabel::construct() -> void {
hwnd = CreateWindow(
L"BUTTON", L"",
WS_CHILD | WS_TABSTOP | BS_RADIOBUTTON,
L"BUTTON", L"", WS_CHILD | WS_TABSTOP | BS_RADIOBUTTON,
0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0
);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
pWidget::_setState();
if(state().checked) setChecked();
setGroup(state().group);
setText(state().text);
}
@@ -18,13 +17,13 @@ auto pRadioLabel::destruct() -> void {
DestroyWindow(hwnd);
}
auto pRadioLabel::minimumSize() -> Size {
auto size = pFont::size(hfont, state().text);
auto pRadioLabel::minimumSize() const -> Size {
auto size = pFont::size(self().font(true), state().text ? state().text : " ");
return {size.width() + 20, size.height() + 4};
}
auto pRadioLabel::setChecked() -> void {
if(auto group = self().group()) {
if(auto& group = state().group) {
for(auto& weak : group->state.objects) {
if(auto object = weak.acquire()) {
if(auto radioLabel = dynamic_cast<mRadioLabel*>(object.data())) {
@@ -38,6 +37,19 @@ auto pRadioLabel::setChecked() -> void {
}
auto pRadioLabel::setGroup(sGroup group) -> void {
bool first = true;
if(auto& group = state().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)(radioLabel->state.checked = first), 0);
first = false;
}
}
}
}
}
}
auto pRadioLabel::setText(const string& text) -> void {

View File

@@ -5,7 +5,7 @@ namespace hiro {
struct pRadioLabel : pWidget {
Declare(RadioLabel, Widget)
auto minimumSize() -> Size;
auto minimumSize() const -> Size override;
auto setChecked() -> void;
auto setGroup(sGroup group) -> void override;
auto setText(const string& text) -> void;

View File

@@ -24,7 +24,7 @@ auto pTabFrameItem::setClosable(bool closable) -> void {
//unsupported
}
auto pTabFrameItem::setIcon(const image& icon) -> void {
auto pTabFrameItem::setImage(const Image& image) -> void {
if(auto parent = _parent()) {
parent->_buildImageList();
}

View File

@@ -8,7 +8,7 @@ struct pTabFrameItem : pObject {
auto append(sLayout layout) -> void;
auto remove(sLayout layout) -> void;
auto setClosable(bool closable) -> void;
auto setIcon(const image& icon) -> void;
auto setImage(const Image& image) -> void;
auto setMovable(bool movable) -> void;
auto setSelected() -> void;
auto setText(const string& text) -> void;

View File

@@ -15,9 +15,10 @@ static auto CALLBACK TabFrame_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPA
}
auto pTabFrame::construct() -> void {
hwnd = CreateWindow(WC_TABCONTROL, L"",
WS_CHILD | WS_TABSTOP,
0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0);
hwnd = CreateWindow(
WC_TABCONTROL, L"", WS_CHILD | WS_TABSTOP,
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);
@@ -38,7 +39,7 @@ auto pTabFrame::append(sTabFrameItem item) -> void {
TabCtrl_InsertItem(hwnd, item->offset(), &tcItem);
if(auto self = item->self()) {
self->setClosable(item->state.closable);
self->setIcon(item->state.icon);
self->setImage(item->state.image);
self->setMovable(item->state.movable);
self->setText(item->state.text);
if(item->selected()) self->setSelected();
@@ -52,10 +53,6 @@ auto pTabFrame::remove(sTabFrameItem item) -> void {
_buildImageList();
}
auto pTabFrame::setEdge(Edge edge) -> void {
//unsupported
}
auto pTabFrame::setEnabled(bool enabled) -> void {
pWidget::setEnabled(enabled);
for(auto& item : state().items) {
@@ -78,6 +75,10 @@ auto pTabFrame::setGeometry(Geometry geometry) -> void {
}
}
auto pTabFrame::setNavigation(Navigation navigation) -> void {
//unsupported
}
auto pTabFrame::setVisible(bool visible) -> void {
pWidget::setVisible(visible);
for(auto& item : state().items) {
@@ -93,13 +94,13 @@ auto pTabFrame::_buildImageList() -> void {
if(imageList) { ImageList_Destroy(imageList); imageList = nullptr; }
imageList = ImageList_Create(size, size, ILC_COLOR32, 1, 0);
for(auto& item : state().items) {
ImageList_Append(imageList, item->state.icon, size);
ImageList_Append(imageList, item->state.image, size);
}
TabCtrl_SetImageList(hwnd, imageList);
for(auto offset : range(state().items)) {
TCITEM tcItem;
tcItem.mask = TCIF_IMAGE;
tcItem.iImage = state().items[offset]->state.icon ? offset : -1;
tcItem.iImage = state().items[offset]->state.image ? offset : -1;
TabCtrl_SetItem(hwnd, offset, &tcItem);
}
}

View File

@@ -7,9 +7,9 @@ struct pTabFrame : pWidget {
auto append(sTabFrameItem item) -> void;
auto remove(sTabFrameItem item) -> void;
auto setEdge(Edge edge) -> void;
auto setEnabled(bool enabled) -> void override;
auto setGeometry(Geometry geometry) -> void override;
auto setNavigation(Navigation navigation) -> void;
auto setVisible(bool visible) -> void override;
auto onChange() -> void;

View File

@@ -11,9 +11,9 @@ auto pTextEdit::construct() -> void {
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
pWidget::_setState();
setBackgroundColor(state().backgroundColor);
setCursorPosition(state().cursorPosition);
setEditable(state().editable);
setText(state().text);
setCursor(state().cursor);
}
auto pTextEdit::destruct() -> void {
@@ -27,9 +27,11 @@ auto pTextEdit::setBackgroundColor(Color color) -> void {
backgroundBrush = CreateSolidBrush(color ? CreateRGB(color) : GetSysColor(COLOR_WINDOW));
}
auto pTextEdit::setCursorPosition(unsigned position) -> void {
if(position == ~0) position >>= 1; //Edit_SetSel takes signed type
Edit_SetSel(hwnd, position, position);
auto pTextEdit::setCursor(Cursor cursor) -> void {
signed end = GetWindowTextLength(hwnd);
signed offset = max(0, min(end, cursor.offset()));
signed length = max(0, min(end, cursor.offset() + cursor.length()));
Edit_SetSel(hwnd, offset, length);
Edit_ScrollCaret(hwnd);
}

View File

@@ -6,7 +6,7 @@ struct pTextEdit : pWidget {
Declare(TextEdit, Widget)
auto setBackgroundColor(Color color) -> void;
auto setCursorPosition(unsigned position) -> void;
auto setCursor(Cursor cursor) -> void;
auto setEditable(bool editable) -> void;
auto setForegroundColor(Color color) -> void;
auto setText(string text) -> void;

View File

@@ -35,11 +35,9 @@ auto pWidget::setFocused() -> void {
SetFocus(hwnd);
}
auto pWidget::setFont(const string&) -> void {
auto font = self().font(true);
if(!font) font = Font::sans(8);
auto pWidget::setFont(const Font&) -> void {
if(hfont) DeleteObject(hfont);
hfont = pFont::create(font);
hfont = pFont::create(self().font(true));
SendMessage(hwnd, WM_SETFONT, (WPARAM)hfont, 0);
}

View File

@@ -9,7 +9,7 @@ struct pWidget : pSizable {
virtual auto minimumSize() -> Size;
auto setEnabled(bool enabled) -> void override;
auto setFocused() -> void;
auto setFont(const string& font) -> void override;
auto setFont(const Font& font) -> void override;
virtual auto setGeometry(Geometry geometry) -> void;
auto setVisible(bool visible) -> void override;

View File

@@ -82,7 +82,7 @@ auto pWindow::setFocused() -> void {
SetFocus(hwnd);
}
auto pWindow::setFont(const string& font) -> void {
auto pWindow::setFont(const Font& font) -> void {
if(auto layout = state().layout) {
if(auto self = layout->self()) self->setFont(layout->font(true));
}

View File

@@ -17,7 +17,7 @@ struct pWindow : pObject {
auto setDroppable(bool droppable) -> void;
auto setEnabled(bool enabled) -> void;
auto setFocused() -> void;
auto setFont(const string& font) -> void override;
auto setFont(const Font& font) -> void override;
auto setFullScreen(bool fullScreen) -> void;
auto setGeometry(Geometry geometry) -> void;
auto setModal(bool modal) -> void;