Files
Sebastian Dine 55ace147bc major: initial release
* initial development

* pipeline

* pipeline

* pipeline

* pipeline

* pipeline

* pipeline

* pipeline

* pipeline

* pipeline

* pipeline

* pipeline

* pipeline

* pipeline

* pipeline

* ci/cd

* ci/cd

* ci/cd

* ci/cd

* ci/cd

* ci/cd

* ci/cd

* pokemon

* pokemon

* pokemon

* pokemon

* pokemon

* pokemon

* improvements

* improvements

* ci/cd

* ci/cd

* improvements

* improvements

* improvements

* improvements

* improvements

* improvements

* improvements

* improvements

* improvements

---------

Co-authored-by: sdine <sdine@sdine.com>
2026-05-09 11:05:47 +02:00

93 lines
3.5 KiB
C++

#pragma once
// IconListCtrl
//
// `wxListCtrl` subclass that custom-draws icon sub-items so they are
// pixel-perfect centered within the cell, matching our themed-header icon
// centering exactly even after column resize.
//
// Why we need this:
// - Native MSW `LVS_REPORT` sub-item image rendering anchors the image at
// the cell's left edge with a small built-in inset. The header icons in
// this app are centered via wx sizers, so the two never line up.
// - We override `NM_CUSTOMDRAW` to paint icons ourselves at the exact
// center of each icon sub-item rect.
// - Default text cell rendering is left untouched.
//
// Rendering path (MSW):
// We keep one premultiplied 32 bpp BGRA DIB section per (iconIdx, selected)
// variant and composite it onto the listctrl's HDC with `AlphaBlend`
// (`AC_SRC_OVER` + `AC_SRC_ALPHA`) from `NM_CUSTOMDRAW`. We deliberately
// do **not** route through `ImageList_Draw` / `HIMAGELIST`: in our test
// environment `ImageList_Draw` on an `ILC_COLOR32` list ignored the alpha
// channel and the "transparent" canvas around each glyph painted as
// opaque black behind the icon — see `ui_wx/AGENTS.md` convention 11.
//
// Usage:
// 1) Construct with the same wxListCtrl flags as before.
// 2) Call `setIconColumns(firstIconCol, count)` so the subclass knows which
// sub-item indices it owns.
// 3) Call `setIconPredicate(...)` with a callback that decides if the icon
// should be drawn for a given (row, iconIdx).
// 4) Call `setIconBitmaps(normal, selected)` with two equally-sized vectors
// of pre-rendered icon bitmaps — one variant per selection state.
//
// This class is MSW-specific in behavior (NM_CUSTOMDRAW). On other platforms
// `MSWOnNotify` is a no-op override and the listctrl falls back to default
// rendering — which is fine because this app only ships on Windows.
#include <wx/bitmap.h>
#include <wx/listctrl.h>
#include <functional>
#include <utility>
#include <vector>
namespace ccm::ui {
class IconListCtrl : public wxListCtrl {
public:
using wxListCtrl::wxListCtrl;
using IconPredicate = std::function<bool(long row, int iconIdx)>;
~IconListCtrl() override;
void setIconColumns(int firstIconCol, int iconCount) noexcept {
firstIconCol_ = firstIconCol;
iconColCount_ = iconCount;
}
void setIconPredicate(IconPredicate p) { predicate_ = std::move(p); }
// Replace the cached icon bitmaps. Both vectors must have the same size
// (one entry per icon column). Internal premultiplied DIB cache rebuilds.
void setIconBitmaps(std::vector<wxBitmap> normal,
std::vector<wxBitmap> selected);
protected:
#ifdef __WXMSW__
bool MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM* result) override;
#endif
private:
int firstIconCol_{-1};
int iconColCount_{0};
IconPredicate predicate_;
std::vector<wxBitmap> normalBmps_;
std::vector<wxBitmap> selectedBmps_;
// Premultiplied BGRA DIB sections used as the source for `AlphaBlend`.
// Stored as `void*` (HBITMAP) so the header stays free of `<windows.h>`.
// Index layout:
// [0 .. iconColCount_) -> normal variants
// [iconColCount_ .. 2 * iconColCount_) -> selected variants
std::vector<void*> dibBitmaps_;
int dibWidth_{0};
int dibHeight_{0};
void rebuildDibCache();
void destroyDibCache();
};
} // namespace ccm::ui