Mercurial > foo_out_sdl
diff foosdk/sdk/libPPUI/CListControl.h @ 1:20d02a178406 default tip
*: check in everything else
yay
| author | Paper <paper@tflc.us> |
|---|---|
| date | Mon, 05 Jan 2026 02:15:46 -0500 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/foosdk/sdk/libPPUI/CListControl.h Mon Jan 05 02:15:46 2026 -0500 @@ -0,0 +1,405 @@ +#pragma once + +// ================================================================================ +// Main CListControl implementation +// +// For ready-to-use CListControl specializations, +// see CListControlSimple.h and CListControlOwnerData.h +// ================================================================================ + + +#pragma comment(lib, "uxtheme.lib") + +#include <functional> +#include <list> +#include <vector> +#include <set> +#include <string> +#include <map> +#include "CMiddleDragImpl.h" +#include "wtl-pp.h" +#include "gesture.h" +#include "gdiplus_helpers.h" + +#define CListControl_ScrollWindowFix + +#ifdef CListControl_ScrollWindowFix +#define WS_EX_COMPOSITED_CListControl 0 +#else +#define WS_EX_COMPOSITED_CListControl WS_EX_COMPOSITED +#endif + + +typedef std::function< bool ( UINT, DWORD, CPoint ) > CaptureProc_t; + +typedef CWinTraits<WS_VSCROLL | WS_HSCROLL | WS_TABSTOP | WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, WS_EX_COMPOSITED_CListControl> CListControlTraits; + +class CListControlImpl : public CWindowImpl<CListControlImpl,CWindow,CListControlTraits> { +public: + typedef uint64_t groupID_t; + + CListControlImpl() {} + + static const wchar_t* GetWndClassName() { return L"libPPUI:CListControl"; } + DECLARE_WND_CLASS_EX(GetWndClassName(), CS_DBLCLKS, (-1)); + + // Wrapper around CWindowImpl::Create(). + // Creates CListControl replacing another dialog control with the specified ID. + // Note that m_dlgWantEnter is set to false by this method, as it's typically unwanted in dialogs. + HWND CreateInDialog( CWindow wndDialog, UINT replaceControlID ); + HWND CreateInDialog(CWindow wndDialog, UINT replaceControlID, CWindow wndReplace); + + enum { + MSG_SIZE_ASYNC = WM_USER + 13, + MSG_EXEC_DEFERRED, + UserMsgBase + }; + static UINT msgSetDarkMode(); + const UINT MSG_SET_DARK = msgSetDarkMode(); + + static void wndSetDarkMode(CWindow wndListControl, bool bDark); + + BEGIN_MSG_MAP_EX(CListControlImpl) + MESSAGE_HANDLER_EX(MSG_EXEC_DEFERRED, OnExecDeferred); + MSG_WM_PAINT(OnPaint) + MSG_WM_PRINTCLIENT(OnPrintClient); + MESSAGE_HANDLER(WM_VSCROLL,OnVScroll); + MESSAGE_HANDLER(WM_HSCROLL,OnHScroll); + MESSAGE_HANDLER(WM_SIZE,OnSize); + MESSAGE_HANDLER(WM_MOUSEHWHEEL,OnHWheel); + MESSAGE_HANDLER(WM_MOUSEWHEEL,OnVWheel); + MESSAGE_HANDLER(WM_LBUTTONDOWN,SetFocusPassThru); + MESSAGE_HANDLER(WM_RBUTTONDOWN,SetFocusPassThru); + MESSAGE_HANDLER(WM_MBUTTONDOWN,SetFocusPassThru); + MESSAGE_HANDLER(WM_LBUTTONDBLCLK,SetFocusPassThru); + MESSAGE_HANDLER(WM_RBUTTONDBLCLK,SetFocusPassThru); + MESSAGE_HANDLER(WM_MBUTTONDBLCLK,SetFocusPassThru); + MESSAGE_HANDLER(WM_CREATE,OnCreatePassThru); + MSG_WM_ERASEBKGND(OnEraseBkgnd) + MESSAGE_HANDLER(MSG_SIZE_ASYNC,OnSizeAsync); + MESSAGE_HANDLER(WM_GESTURE, OnGesture) + MSG_WM_THEMECHANGED(OnThemeChanged) + MESSAGE_HANDLER_EX( WM_GETDLGCODE, OnGetDlgCode ) + MESSAGE_HANDLER_EX( MSG_SET_DARK, OnSetDark ) + MSG_WM_KEYDOWN(OnKeyDown) + END_MSG_MAP() + + virtual void ReloadData(); + virtual void ReloadItems(pfc::bit_array const & mask); + + //! Hookable function called in response to reordering of items. Redraws the view and updates internal data to reflect the change. + virtual void OnItemsReordered( const size_t * order, size_t count ); + //! Hookable function called in response to removal of items. Redraws the view and updates internal data to reflect the change. + virtual void OnItemsRemoved(pfc::bit_array const& mask, size_t oldCount) { (void)mask; (void)oldCount; ReloadData(); } + //! Hookable function called in response to insertion of items. Redraws the view and updates internal data to reflect the change. + virtual void OnItemsInsertedEx(pfc::bit_array const& mask, size_t oldCount, size_t newCount, bool bSelect) { (void)mask; (void)oldCount; (void) newCount; (void)bSelect; ReloadData(); } + void OnItemsInserted(size_t at, size_t count, bool bSelect); + + //! Helper around OnItemsRemoved() + void OnItemRemoved(size_t which); + + void ReloadItem(size_t i) { ReloadItems( pfc::bit_array_one(i) ); } + void OnViewAreaChanged() {OnViewAreaChanged(GetViewOrigin());} + void OnViewAreaChanged(CPoint p_originOverride); + void UpdateGroupHeader2(size_t atItem); + void UpdateItems(const pfc::bit_array & p_mask); + void UpdateItemsAndHeaders(const pfc::bit_array & p_mask); + void UpdateItem(t_size p_item) {UpdateItems(pfc::bit_array_one(p_item));} + void UpdateItemsAll() {Invalidate();} + void EnsureItemVisible(t_size p_item, bool bUser = false); + void EnsureHeaderVisible2(size_t atItem); + virtual void EnsureVisibleRectAbs(const CRect & p_rect); + CRect GetItemRect(t_size p_item) const; + bool GetGroupHeaderRect2(size_t atItem,CRect & p_rect) const; + CRect GetItemRectAbs(t_size p_item) const; + int GetItemOffsetAbs(size_t item) const; + int GetItemOffsetAbs2(size_t from, size_t to) const; + int GetItemBottomOffsetAbs(size_t item) const; + int GetItemHeightCached(size_t item) const; + int GetItemContentHeightCached(size_t item) const; + bool GetGroupHeaderRectAbs2(size_t atItem,CRect & p_rect) const; + CPoint GetViewOrigin() const {return m_viewOrigin;} + CPoint GetViewOffset() const {return GetViewOrigin() - GetClientOrigin();} + CPoint PointAbsToClient(CPoint pt) const; + CPoint PointClientToAbs(CPoint pt) const; + CRect RectAbsToClient(CRect rc) const; + CRect RectClientToAbs(CRect rc) const; + int GetViewAreaWidth() const {return GetItemWidth();} + int GetViewAreaHeight() const; + CRect GetViewAreaRectAbs() const; + CRect GetViewAreaRect() const; + CRect GetValidViewOriginArea() const; + bool GetItemRangeAbs(const CRect & p_rect,t_size & p_base,t_size & p_count) const; + bool GetItemRangeAbsInclHeaders(const CRect & p_rect,t_size & p_base,t_size & p_count) const; + bool GetItemRange(const CRect & p_rect,t_size & p_base,t_size & p_count) const; + void MoveViewOriginNoClip(CPoint p_target); + void MoveViewOrigin(CPoint p_target); + CPoint ClipViewOrigin(CPoint p_origin) const; + void MoveViewOriginDelta(CPoint p_delta) {MoveViewOrigin( GetViewOrigin() + p_delta );} + void MoveViewOriginDeltaNoClip(CPoint p_delta) {MoveViewOriginNoClip( GetViewOrigin() + p_delta );} + bool ItemFromPoint(CPoint const & p_pt, t_size & p_item) const; + size_t ItemFromPoint(CPoint const& pt) const; + bool GroupHeaderFromPoint2(CPoint const & p_pt,size_t & p_atItem) const {return GroupHeaderFromPointAbs2( PointClientToAbs(p_pt),p_atItem);} + bool ItemFromPointAbs(CPoint const & p_pt,t_size & p_item) const; + size_t ItemFromPointAbs(CPoint const& p_pt) const; + bool GroupHeaderFromPointAbs2(CPoint const & p_pt,size_t & p_atItem) const; + size_t IndexFromPointAbs(CPoint pt) const; + size_t IndexFromPointAbs(int ptY) const; + + size_t ResolveGroupRange2(t_size p_base) const; + bool ResolveGroupRangeCached(size_t itemInGroup, size_t & outBegin, size_t & outEnd) const; + + virtual int GetGroupHeaderHeight() const {return 0;} + virtual int GetItemHeight() const {return 0;} + + // Variable height items - return -1 to use generic. + // This is intended to be used scarcely. Layout engine assumes that majority of items use the generic height. + // Heavy use of variable height items on large data sets will lead to performance issues. + virtual int GetItemHeight2(size_t which) const; + + // Companion to GetItemHeight2(), returns portion of GetItemHeight2()-defined item which the item content actually occupies + virtual int GetItemHeight2Content(size_t which, int iHeight) const { (void)which; return iHeight; } + + virtual int GetMinGroupHeight() const { return 0; } + virtual int GetMinGroupHeight2(size_t itemInGroup) const { (void)itemInGroup; return GetMinGroupHeight(); } + void MinGroupHeight2Changed(size_t itemInGroup, bool reloadWhole = false); + void MinGroupHeight2ChangedForGroup(groupID_t group, bool reloadWhole = false); + virtual int GetItemWidth() const {return 0;} + virtual t_size GetItemCount() const {return 0;} + + bool IsItemFirstInGroupCached(size_t item) const; + bool IsItemFirstInGroup(size_t item) const; + bool IsItemLastInGroup(size_t item) const; + + virtual groupID_t GetItemGroup(t_size p_item) const { (void)p_item; return 0; } + + virtual void RenderRect(const CRect& p_rect, CDCHandle p_dc); + //override optionally + virtual void RenderItem(t_size p_item,const CRect & p_itemRect,const CRect & p_updateRect,CDCHandle p_dc); + //override optionally + virtual void RenderGroupHeader2(size_t p_item,const CRect & p_headerRect,const CRect & p_updateRect,CDCHandle p_dc); + + virtual void RenderGroupOverlay(size_t baseItem, const CRect& p_groupWhole, const CRect& p_updateRect, CDCHandle p_dc) { (void)baseItem; (void)p_groupWhole; (void)p_updateRect; (void)p_dc; } + void UpdateGroupOverlayByID(groupID_t groupID, int xFrom = 0, int xTo = -1); + bool GetGroupOverlayRectAbs(size_t atItem, CRect & outRect); + + //called by default RenderItem implementation + virtual void RenderItemText(t_size p_item, const CRect& p_itemRect, const CRect& p_updateRect, CDCHandle p_dc, bool allowColors) { (void)p_item; (void)p_itemRect; (void)p_updateRect; (void)p_dc; (void)allowColors; } + //called by default RenderItem implementation + virtual void RenderGroupHeaderText2(size_t p_item, const CRect& p_headerRect, const CRect& p_updateRect, CDCHandle p_dc) { (void)p_item; (void)p_headerRect; (void)p_updateRect; (void)p_dc; } + + virtual void RenderItemBackground(CDCHandle p_dc,const CRect & p_itemRect,size_t item, uint32_t bkColor); + virtual void RenderGroupHeaderBackground(CDCHandle p_dc,const CRect & p_headerRect,int iGroup); + + virtual void RenderBackground( CDCHandle dc, CRect const & rc ); + + virtual void OnViewOriginChange(CPoint p_delta) { (void)p_delta; } + + // RenderOverlay2 takes rect in client coords, not absolute + // p_dc operates on client coords also + virtual void RenderOverlay2(const CRect& p_updaterect, CDCHandle p_dc) { (void)p_updaterect; (void)p_dc; } + + virtual bool FixedOverlayPresent() {return false;} + + virtual CRect GetClientRectHook() const; + + enum { + colorText = COLOR_WINDOWTEXT, + colorBackground = COLOR_WINDOW, + colorHighlight = COLOR_HOTLIGHT, + colorSelection = COLOR_HIGHLIGHT, + }; + + virtual COLORREF GetSysColorHook( int colorIndex ) const; + + //! Called by CListControlWithSelectionBase. + virtual void OnItemClicked(t_size item, CPoint pt) { (void)item; (void)pt; } + //! Called by CListControlWithSelectionBase. + virtual void OnGroupHeaderClicked(groupID_t groupId, CPoint pt) { (void)groupId; (void)pt; } + + //! Return true to indicate that some area of the control has a special purpose and clicks there should not trigger changes in focus/selection. + virtual bool OnClickedSpecialHitTest(CPoint pt) { (void)pt; return false; } + virtual bool OnClickedSpecial(DWORD status, CPoint pt) { (void)status; (void)pt; return false; } + + virtual bool AllowScrollbar(bool vertical) const { (void)vertical; return true; } + + CPoint GetClientOrigin() const {return GetClientRectHook().TopLeft();} + int GetVisibleHeight() const { return GetClientRectHook().Height(); } + + bool IsItemVisible(size_t which) const; + //! Returns first visible item, first invisible item, begin-end style + std::pair<size_t, size_t> GetVisibleRange() const; + CRect GetVisibleRectAbs() const { + return this->RectClientToAbs(GetClientRectHook()); + } + + bool IsSameItemOrHeaderAbs(const CPoint & p_point1, const CPoint & p_point2) const; + + void AddItemToUpdateRgn(HRGN p_rgn, t_size p_index) const; + void AddGroupHeaderToUpdateRgn2(HRGN p_rgn, size_t atItem) const; + + t_size InsertIndexFromPoint(const CPoint & p_pt) const; + //! Translate point to insert location for drag and drop. \n + //! Made virtual so it can be specialized to allow only specific drop locations. + virtual t_size InsertIndexFromPointEx(const CPoint & pt, bool & bInside) const; + + virtual void ListHandleResize(); + + //! Can smooth-scroll *now* ? Used to suppress smooth scroll on temporary basis due to specific user operations in progress + virtual bool CanSmoothScroll() const { return true; } + //! Is smooth scroll enabled by user? + virtual bool UserEnabledSmoothScroll() const; + virtual bool ToggleSelectedItemsHook(pfc::bit_array const& mask) { (void)mask; return false; } + + + SIZE GetDPI() const { return this->m_dpi;} + + // Should this control take enter key in dialogs or not? + // Default to true for compatibility with existing code - but when used in dialogs, you'll want it set to false to hit [OK] with enter. + // Note that CreateInDialog() sets this to false. Change it later if you want enter key presses to reach this control in a dialog. + bool m_dlgWantEnter = true; + + bool WantReturn() const { return m_dlgWantEnter; } + void SetWantReturn(bool v) { m_dlgWantEnter = v; } + + enum { + rowStyleGrid = 0, + rowStyleFlat, + rowStylePlaylist, + rowStylePlaylistDelimited, + + + rowStyleDefault = rowStylePlaylistDelimited + }; + void SetPlaylistStyle() {SetRowStyle(rowStylePlaylist);} + void SetRowStyle(unsigned v) { if (m_rowStyle == v) return; this->m_rowStyle = v; if (m_hWnd) Invalidate(); } + void SetFlatStyle() {SetRowStyle(rowStyleFlat);} + unsigned m_rowStyle = rowStyleDefault; + bool DelimitColumns() const { return m_rowStyle == rowStyleGrid || m_rowStyle == rowStylePlaylistDelimited; } + + static COLORREF BlendGridColor( COLORREF bk, COLORREF tx ); + static COLORREF BlendGridColor( COLORREF bk ); + COLORREF GridColor(); + + void SetDarkMode(bool bDark); + virtual void RefreshDarkMode(); + bool GetDarkMode() const { return m_darkMode; } +private: + void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); + LRESULT OnSetDark(UINT, WPARAM, LPARAM); + int HandleWheel(int & p_accum,int p_delta, bool bHoriz); + + void PaintContent(CRect rcPaint, HDC dc); + void OnPrintClient(HDC dc, UINT uFlags); + void OnPaint(CDCHandle); + LRESULT OnVScroll(UINT,WPARAM,LPARAM,BOOL&); + LRESULT OnHScroll(UINT,WPARAM,LPARAM,BOOL&); + LRESULT OnSize(UINT,WPARAM,LPARAM,BOOL&); + LRESULT OnSizeAsync(UINT,WPARAM,LPARAM,BOOL&) {ListHandleResize();return 0;} + LRESULT OnVWheel(UINT,WPARAM,LPARAM,BOOL&); + LRESULT OnHWheel(UINT,WPARAM,LPARAM,BOOL&); + LRESULT OnGesture(UINT,WPARAM,LPARAM,BOOL&); + LRESULT SetFocusPassThru(UINT,WPARAM,LPARAM,BOOL&); + LRESULT OnCreatePassThru(UINT,WPARAM,LPARAM,BOOL&); + BOOL OnEraseBkgnd(CDCHandle); + LRESULT OnGetDlgCode(UINT, WPARAM, LPARAM); + + void OnThemeChanged(); + int GetScrollThumbPos(int which); + void RefreshSliders(); + void RefreshSlider(bool p_vertical); + + void OnSizeAsync_Trigger(); + static LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam); + bool MouseWheelFromHook(UINT msg, LPARAM data); + + bool m_suppressMouseWheel = false; + int m_wheelAccumX = 0, m_wheelAccumY = 0; + CPoint m_viewOrigin = CPoint(0,0); + bool m_sizeAsyncPending = false; + CPoint m_gesturePoint; + + // Prepares group header & variable height item data for view with the specified origin and current client size + // Returns true if layout cache has changed, false otherwise. + // Updates ptOrigin if necessary + bool PrepLayoutCache(CPoint & ptOrigin, size_t indexLo = SIZE_MAX, size_t indexHi = SIZE_MAX); + std::map<size_t, int> m_varItemHeights; + std::set<size_t> m_groupHeaders; + + size_t FindGroupBaseCached(size_t itemFor) const; + size_t FindGroupBase(size_t itemFor) const; + size_t FindGroupBase(size_t itemFor, groupID_t group) const; + +protected: + // Grouped layout mode toggle + // Default mode is greedy, probes whole list layout in advance (no glitches when scrolling) + // In special conditions when probing is expensive, greedy mode should be turned off + bool m_greedyGroupLayout = true; + + pfc::map_t<pfc::string8, CTheme, pfc::comparator_strcmp> m_themeCache; + CTheme & themeFor( const char * what ); + CTheme & theme() { return themeFor("LISTVIEW");} + + const SIZE m_dpi = QueryScreenDPIEx(); + CGestureAPI m_gestureAPI; + bool m_ensureVisibleUser = false; + + void defer( std::function<void () > f ); + LRESULT OnExecDeferred(UINT, WPARAM, LPARAM); + + // Overlays our stuff on top of generic DoDragDrop call. + // Currently catches mouse wheel messages in mid-drag&drop and handles them in our view. + HRESULT DoDragDrop(LPDATAOBJECT pDataObj, LPDROPSOURCE pDropSource, DWORD dwOKEffects, LPDWORD pdwEffect); + + bool paintInProgress() const { return m_paintInProgress; } +private: + bool m_defferredMsgPending = false; + std::list<std::function<void ()> > m_deferred; + bool m_darkMode = false; + + bool m_paintInProgress = false; +}; + +class CListControlFontOps : public CListControlImpl { +private: + typedef CListControlImpl TParent; +public: + CListControlFontOps(); + BEGIN_MSG_MAP_EX(CListControlFontOps) + MESSAGE_HANDLER(WM_GETFONT,OnGetFont); + MESSAGE_HANDLER(WM_SETFONT,OnSetFont); + CHAIN_MSG_MAP(TParent); + END_MSG_MAP() + CFontHandle GetFont() const { return m_font; } + void SetFont(HFONT font, bool bUpdateView = true); +protected: + CFontHandle GetGroupHeaderFont() const {return (HFONT)m_groupHeaderFont;} + virtual double GroupHeaderFontScale() const { return 1.25; } + virtual int GroupHeaderFontWeight(int origVal) const { + //return pfc::min_t<int>(FW_BLACK, origVal + 200); + return origVal; + } + + //! Overridden implementations should always forward the call to the base class. + virtual void OnSetFont(bool bUpdatingView) { (void)bUpdatingView; } + + int GetGroupHeaderHeight() const override {return m_groupHeaderHeight;} + int GetItemHeight() const override {return m_itemHeight;} + +private: + LRESULT OnSetFont(UINT,WPARAM,LPARAM,BOOL&); + LRESULT OnGetFont(UINT,WPARAM,LPARAM,BOOL&); + void UpdateGroupHeaderFont(); + void CalculateHeights(); + int m_itemHeight, m_groupHeaderHeight; + CFontHandle m_font; + CFont m_groupHeaderFont; +}; + +#include "CListControlHeaderImpl.h" +#include "CListControlTruncationTooltipImpl.h" + + + +typedef CMiddleDragImpl<CListControlTruncationTooltipImpl> CListControl; +
