Themed checkboxes

Slightly larger items in (multiple) choice viewers
This commit is contained in:
Twan van Laarhoven
2020-05-06 02:47:02 +02:00
parent 6161feefc5
commit b4435e5e57
11 changed files with 136 additions and 147 deletions
+4 -4
View File
@@ -20,7 +20,7 @@ class DropDownHider;
/// A popup/drop down window displaying a list of items
/** This class is an abstract base for various drop down lists */
class DropDownList : public wxPopupWindow {
public:
public:
~DropDownList();
/// Create a drop down list, possibly a sub menu
/** the viewer will be notified to redraw its drop down icon */
@@ -40,7 +40,7 @@ class DropDownList : public wxPopupWindow {
/// Takes a mouse event from the parent, show/hide as appropriate
bool onMouseInParent(wxMouseEvent&, bool open_in_place);
protected:
protected:
/// Prepare for showing the list
virtual void onShow() {}
@@ -85,7 +85,7 @@ class DropDownList : public wxPopupWindow {
RealSize item_size; ///< Size of an item;
RealSize icon_size; ///< Size of icons;
private:
private:
// --------------------------------------------------- : Data
size_t selected_item; ///< The item that is selected, or NO_SELECTION
@@ -124,7 +124,7 @@ class DropDownList : public wxPopupWindow {
void draw(DC& dc);
void drawItem(DC& dc, int y, size_t item);
protected:
protected:
virtual void redrawArrowOnParent(); // allow override
};
+1 -2
View File
@@ -141,8 +141,7 @@ void ThumbnailThread::request(const ThumbnailRequestP& request) {
worker->Create();
worker->Run();
}
}
else {
} else {
Image img;
try {
img = request->generate();
+39 -34
View File
@@ -382,6 +382,17 @@ wxImage load_resource_tool_image(const String& name) {
// ----------------------------------------------------------------------------- : Platform look
#if wxUSE_UXTHEME && defined(__WXMSW__)
RECT msw_rect(wxRect const& rect, int dl = 0, int dt = 0, int dr = 0, int db=0) {
RECT r;
r.left = rect.x - dl;
r.top = rect.y - dt;
r.right = rect.x + rect.width + dr;
r.bottom = rect.y + rect.height + db;
return r;
}
#endif
// Draw a basic 3D border
void draw3DBorder(DC& dc, int x1, int y1, int x2, int y2) {
dc.SetPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DDKSHADOW));
@@ -400,14 +411,10 @@ void draw3DBorder(DC& dc, int x1, int y1, int x2, int y2) {
void draw_control_box(Window* win, DC& dc, const wxRect& rect, bool focused, bool enabled) {
#if wxUSE_UXTHEME && defined(__WXMSW__)
RECT r;
RECT r = msw_rect(rect, 1,1,1,1);
if (wxUxThemeIsActive()) {
HTHEME hTheme = (HTHEME)::OpenThemeData(GetHwndOf(win), L"EDIT");
HTHEME hTheme = (HTHEME)::OpenThemeData(GetHwndOf(win), VSCLASS_EDIT);
if (hTheme) {
r.left = rect.x -1;
r.top = rect.y -1;
r.right = rect.x + rect.width + 1;
r.bottom = rect.y + rect.height + 1;
::DrawThemeBackground(
hTheme,
(HDC)dc.GetHDC(),
@@ -478,25 +485,34 @@ void draw_drop_down_arrow(Window* win, DC& dc, const wxRect& rect, bool active)
, active ? wxCONTROL_PRESSED : 0);
}
void draw_checkbox(Window* win, DC& dc, const wxRect& rect, bool checked, bool enabled) {
#if wxUSE_UXTHEME && defined(__WXMSW__)
// TODO: Windows version?
void draw_checkbox(const Window* win, DC& dc, const wxRect& rect, bool checked, bool enabled) {
#if defined(__WXMSW__)
if (win == nullptr) win = dc.GetWindow();
#endif
// portable version
if (checked) {
dc.DrawCheckMark(wxRect(rect.x-1,rect.y-1,rect.width+2,rect.height+2));
if (win) {
wxRendererNative& rn = wxRendererNative::GetDefault();
rn.DrawCheckBox(const_cast<wxWindow*>(win), dc, rect, (checked ? wxCONTROL_CHECKED : 0) | (enabled ? 0 : wxCONTROL_DISABLED));
return;
} else {
// portable version
if (checked) {
dc.DrawCheckMark(wxRect(rect.x+1,rect.y+1,rect.width-2,rect.height-2));
}
dc.SetPen(wxSystemSettings::GetColour(enabled ? wxSYS_COLOUR_WINDOWTEXT: wxSYS_COLOUR_GRAYTEXT));
dc.SetBrush(*wxTRANSPARENT_BRUSH);
dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height);
}
dc.SetPen(wxSystemSettings::GetColour(enabled ? wxSYS_COLOUR_WINDOWTEXT: wxSYS_COLOUR_GRAYTEXT));
dc.SetBrush(*wxTRANSPARENT_BRUSH);
dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height);
}
void draw_radiobox(Window* win, DC& dc, const wxRect& rect, bool checked, bool enabled) {
#if wxUSE_UXTHEME && defined(__WXMSW__) && TODO_FIX_THEME_ENGINE
// TODO: Windows version?
void draw_radiobox(const Window* win, DC& dc, const wxRect& rect, bool checked, bool enabled) {
#if defined(__WXMSW__)
if (win == nullptr) win = dc.GetWindow();
#endif
// portable version
#if 1
if (win) {
wxRendererNative& rn = wxRendererNative::GetDefault();
rn.DrawRadioBitmap(const_cast<wxWindow*>(win), dc, rect, (checked ? wxCONTROL_CHECKED : 0) | (enabled ? 0 : wxCONTROL_DISABLED));
return;
} else {
// circle drawing on windows looks absolutely horrible
// so use rounded rectangles instead
dc.SetPen(wxSystemSettings::GetColour(enabled ? wxSYS_COLOUR_WINDOWTEXT: wxSYS_COLOUR_GRAYTEXT));
@@ -509,25 +525,14 @@ void draw_radiobox(Window* win, DC& dc, const wxRect& rect, bool checked, bool e
//dc.DrawEllipse(rect.x+2,rect.y+2,rect.width-4,rect.height-4);
dc.DrawRoundedRectangle(rect.x+3, rect.y+3, rect.width-6, rect.height-6, rect.width*0.5-4);
}
#endif
}
}
void draw_selection_rectangle(Window* win, DC& dc, const wxRect& rect, bool selected, bool focused, bool hot) {
#if wxUSE_UXTHEME && defined(__WXMSW__)
#if !defined(NTDDI_LONGHORN) || NTDDI_VERSION < NTDDI_LONGHORN
#define LISS_NORMAL LIS_NORMAL
#define LISS_SELECTED LIS_SELECTED
#define LISS_SELECTEDNOTFOCUS LIS_SELECTEDNOTFOCUS
#define LISS_HOT LISS_NORMAL
#define LISS_HOTSELECTED LISS_SELECTED
#endif
HTHEME hTheme = (HTHEME)::OpenThemeData(GetHwndOf(win), L"LISTVIEW");
HTHEME hTheme = (HTHEME)::OpenThemeData(GetHwndOf(win), VSCLASS_LISTVIEW);
if (hTheme) {
RECT r;
r.left = rect.x;
r.top = rect.y;
r.right = rect.x + rect.width;
r.bottom = rect.y + rect.height;
RECT r = msw_rect(rect);
::DrawThemeBackground(
hTheme,
(HDC)dc.GetHDC(),
+2 -2
View File
@@ -97,10 +97,10 @@ void draw_menu_arrow(Window* win, DC& dc, const wxRect& rect, bool active);
void draw_drop_down_arrow(Window* win, DC& dc, const wxRect& rect, bool active);
/// Draws a check box
void draw_checkbox(Window* win, DC& dc, const wxRect& rect, bool checked, bool enabled = true);
void draw_checkbox(const Window* win, DC& dc, const wxRect& rect, bool checked, bool enabled = true);
/// Draws a radio button
void draw_radiobox(Window* win, DC& dc, const wxRect& rect, bool checked, bool enabled = true);
void draw_radiobox(const Window* win, DC& dc, const wxRect& rect, bool checked, bool enabled = true);
/// Draws a (fancy) selection rectangle
void draw_selection_rectangle(Window* win, DC& dc, const wxRect& rect, bool selected = true, bool focused = true, bool hot = false);
+34 -58
View File
@@ -14,10 +14,13 @@
#include <script/image.hpp>
#include <wx/imaglist.h>
const int thumbnail_size = 18;
const double min_item_size = thumbnail_size;
// ----------------------------------------------------------------------------- : ChoiceThumbnailRequest
class ChoiceThumbnailRequest : public ThumbnailRequest {
public:
public:
ChoiceThumbnailRequest(ValueViewer* cve, int id, bool from_disk, bool thread_safe);
virtual Image generate();
virtual void store(const Image&);
@@ -47,44 +50,16 @@ Image ChoiceThumbnailRequest::generate() {
String name = canonical_name_form(s.field().choices->choiceName(id));
ScriptableImage& img = s.choice_images[name];
return img.isReady()
? img.generate(GeneratedImage::Options(16,16, &viewer().getStylePackage(), &viewer().getLocalPackage(), ASPECT_BORDER, true))
? img.generate(GeneratedImage::Options(thumbnail_size, thumbnail_size, &viewer().getStylePackage(), &viewer().getLocalPackage(), ASPECT_BORDER, true))
: wxImage();
}
void ChoiceThumbnailRequest::store(const Image& img) {
ChoiceStyle& s = style();
wxImageList* il = s.thumbnails;
while (id > il->GetImageCount()) {
il->Add(wxBitmap(16,16),*wxBLACK);
}
if (img.Ok()) {
#ifdef __WXMSW__
// for some reason windows doesn't like completely transparent images if they do not have a mask
// HACK:
if (img.HasAlpha() && img.GetWidth() == 16 && img.GetHeight() == 16) {
// is the image empty?
bool empty = true;
int* b = (int*)img.GetAlpha();
int* e = b + 16*16/sizeof(int);
while (b != e) {
if (*b++) {
empty = false;
break;
}
}
// if so, use a mask instead
if (empty) {
const_cast<Image&>(img).ConvertAlphaToMask();
}
}
// Hack ends here
#endif
if (id == il->GetImageCount()) {
il->Add(img);
} else {
il->Replace(id, img);
}
s.thumbnails_status[id] = THUMB_OK;
ChoiceThumbnail& thumbnail = style().thumbnails[id];
ChoiceThumbnailLock lock(thumbnail.mutex);
thumbnail.bitmap = img;
thumbnail.status = THUMB_OK;
}
}
@@ -96,9 +71,9 @@ DropDownChoiceListBase::DropDownChoiceListBase
, cve(cve)
, group(group)
{
icon_size.width = 16;
icon_size.height = 16;
item_size.height = max(16., item_size.height);
icon_size.width = min_item_size;
icon_size.height = min_item_size;
item_size.height = max(min_item_size, item_size.height);
}
void DropDownChoiceListBase::onShow() {
@@ -151,9 +126,6 @@ DropDownList* DropDownChoiceListBase::submenu(size_t item) const {
}
void DropDownChoiceListBase::drawIcon(DC& dc, int x, int y, size_t item, bool selected) const {
// imagelist to use
wxImageList* il = style().thumbnails;
assert(il);
// find the image for the item
int image_id;
if (isFieldDefault(item)) {
@@ -162,22 +134,21 @@ void DropDownChoiceListBase::drawIcon(DC& dc, int x, int y, size_t item, bool se
image_id = getChoice(item)->first_id;
}
// draw image
if (image_id < il->GetImageCount()) {
il->Draw(image_id, dc, x, y, itemEnabled(item) ? wxIMAGELIST_DRAW_NORMAL : wxIMAGELIST_DRAW_TRANSPARENT);
if (image_id < style().thumbnails.size()) {
auto const& thumbnail = style().thumbnails[image_id];
if (thumbnail.status == THUMB_OK)
dc.DrawBitmap(thumbnail.bitmap, x, y);
//il->Draw(image_id, dc, x, y, itemEnabled(item) ? wxIMAGELIST_DRAW_NORMAL : wxIMAGELIST_DRAW_TRANSPARENT);
}
}
void DropDownChoiceListBase::generateThumbnailImages() {
if (!isRoot()) return;
if (!style().thumbnails) {
style().thumbnails = new wxImageList(16,16);
}
int image_count = style().thumbnails->GetImageCount();
int end = group->lastId();
// init choice images
Context& ctx = cve.viewer.getContext();
if (style().choice_images.empty() && style().image.isScripted()) {
for (int i = 0 ; i < end ; ++i) {
int n = field().choices->lastId();
for (int i = 0 ; i < n; ++i) {
try {
String name = canonical_name_form(field().choices->choiceName(i));
ctx.setVariable(_("input"), to_script(name));
@@ -188,21 +159,26 @@ void DropDownChoiceListBase::generateThumbnailImages() {
}
}
}
// init thumbnail vector
if (style().thumbnails.empty()) {
style().thumbnails.resize(field().choices->lastId());
}
assert(style().thumbnails.size() == field().choices->lastId());
// request thumbnails
style().thumbnails_status.resize(end, THUMB_NOT_MADE);
for (int i = 0 ; i < end ; ++i) {
ThumbnailStatus& status = style().thumbnails_status[i];
if (i >= image_count || status != THUMB_OK) {
int end = group->lastId();
for (int i = group->first_id ; i < end ; ++i) {
auto& thumbnail = style().thumbnails[i];
ChoiceThumbnailLock lock(thumbnail.mutex);
if (thumbnail.status != THUMB_OK) {
// update image
ChoiceStyle& s = style();
String name = canonical_name_form(s.field().choices->choiceName(i));
ScriptableImage& img = s.choice_images[name];
if (!img.update(ctx) && status == THUMB_CHANGED) {
status = THUMB_OK; // no need to rebuild
String name = canonical_name_form(field().choices->choiceName(i));
ScriptableImage& img = style().choice_images[name];
if (!img.update(ctx) && thumbnail.status == THUMB_CHANGED) {
thumbnail.status = THUMB_OK; // no need to rebuild
} else if (img.isReady()) {
// request this thumbnail
thumbnail_thread.request(make_intrusive<ChoiceThumbnailRequest>(
&cve, i, status == THUMB_NOT_MADE && !img.local(), img.threadSafe()
&cve, i, thumbnail.status == THUMB_NOT_MADE && !img.local(), img.threadSafe()
));
}
}
+11 -11
View File
@@ -44,22 +44,22 @@ class ChoiceValueEditor : public ChoiceValueViewer, public ValueEditor {
/// A drop down list of choices
/** This is a base class, used for single and multiple choice fields */
class DropDownChoiceListBase : public DropDownList {
public:
public:
DropDownChoiceListBase(Window* parent, bool is_submenu, ValueViewer& cve, ChoiceField::ChoiceP group);
protected:
virtual void onShow();
virtual size_t itemCount() const;
virtual bool lineBelow(size_t item) const;
virtual bool itemEnabled(size_t item) const;
virtual String itemText(size_t item) const;
virtual void drawIcon(DC& dc, int x, int y, size_t item, bool selected) const;
virtual DropDownList* submenu(size_t item) const;
protected:
void onShow() override;
size_t itemCount() const override;
bool lineBelow(size_t item) const override;
bool itemEnabled(size_t item) const override;
String itemText(size_t item) const override;
void drawIcon(DC& dc, int x, int y, size_t item, bool selected) const override;
DropDownList* submenu(size_t item) const override;
protected:
protected:
virtual DropDownList* createSubMenu(ChoiceField::ChoiceP group) const = 0;
private:
private:
DECLARE_EVENT_TABLE();
ValueViewer& cve; ///< Editor this list belongs to
+15 -17
View File
@@ -16,23 +16,23 @@
/// A drop down list of color choices
class DropDownMultipleChoiceList : public DropDownChoiceListBase {
public:
public:
DropDownMultipleChoiceList(Window* parent, bool is_submenu, ValueViewer& cve, ChoiceField::ChoiceP group);
protected:
virtual void onShow();
virtual void select(size_t item);
virtual size_t selection() const;
virtual bool stayOpen(size_t selection) const { return true; }
virtual DropDownList* createSubMenu(ChoiceField::ChoiceP group) const;
virtual void drawIcon(DC& dc, int x, int y, size_t item, bool selected) const;
protected:
void onShow() override;
void select(size_t item) override;
size_t selection() const override;
bool stayOpen(size_t selection) const override { return true; }
DropDownList* createSubMenu(ChoiceField::ChoiceP group) const override;
void drawIcon(DC& dc, int x, int y, size_t item, bool selected) const override;
};
DropDownMultipleChoiceList::DropDownMultipleChoiceList
(Window* parent, bool is_submenu, ValueViewer& cve, ChoiceField::ChoiceP group)
: DropDownChoiceListBase(parent, is_submenu, cve, group)
{
icon_size.width += 16;
icon_size.width += icon_size.height; // make room for checkbox
}
void DropDownMultipleChoiceList::select(size_t item) {
@@ -59,17 +59,15 @@ void DropDownMultipleChoiceList::drawIcon(DC& dc, int x, int y, size_t item, boo
active = dynamic_cast<MultipleChoiceValueEditor&>(cve).value().value.isDefault();
}
// draw checkbox
dc.SetPen(*wxTRANSPARENT_PEN);
dc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
dc.DrawRectangle(x,y,16,16);
wxRect rect = RealRect(x+2,y+2,12,12);
int size = (int)icon_size.height;
wxRect rect = RealRect(x+1,y+1,size-2,size-2);
if (radio) {
draw_radiobox(nullptr, dc, rect, active, itemEnabled(item));
draw_radiobox(this, dc, rect, active, itemEnabled(item));
} else {
draw_checkbox(nullptr, dc, rect, active, itemEnabled(item));
draw_checkbox(this, dc, rect, active, itemEnabled(item));
}
// draw icon
DropDownChoiceListBase::drawIcon(dc, x + 16, y, item, selected);
DropDownChoiceListBase::drawIcon(dc, x + size, y, item, selected);
}
void DropDownMultipleChoiceList::onShow() {
@@ -104,7 +102,7 @@ DropDownList& MultipleChoiceValueEditor::initDropDown() {
void MultipleChoiceValueEditor::determineSize(bool force_fit) {
if (!nativeLook()) return;
// item height
item_height = 16;
item_height = 18;
// height depends on number of items and item height
int item_count = field().choices->lastId();
style().height = item_count * item_height;
+2 -2
View File
@@ -221,9 +221,9 @@ void DropDownWordList::drawIcon(DC& dc, int x, int y, size_t item, bool selected
dc.DrawRectangle(x,y,16,16);
wxRect rect = RealRect(x+2,y+2,12,12);
if (radio) {
draw_radiobox(nullptr, dc, rect, items[item].active(), itemEnabled(item));
draw_radiobox(this, dc, rect, items[item].active(), itemEnabled(item));
} else {
draw_checkbox(nullptr, dc, rect, items[item].active(), itemEnabled(item));
draw_checkbox(this, dc, rect, items[item].active(), itemEnabled(item));
}
}
}