diff foosdk/sdk/libPPUI/CListControlWithSelection.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/CListControlWithSelection.h	Mon Jan 05 02:15:46 2026 -0500
@@ -0,0 +1,286 @@
+#pragma once
+
+#include "CListControl.h"
+
+//! Implementation of focus/selection handling. Leaves maintaining focus/selection info to the derived class. \n
+//! Most classes should derive from CListControlWithSelectionImpl instead.
+class CListControlWithSelectionBase : public CListControl {
+public:
+	typedef CListControl TParent;
+	CListControlWithSelectionBase() {}
+	BEGIN_MSG_MAP_EX(CListControlWithSelectionBase)
+		MSG_WM_CREATE(OnCreatePassThru);
+		MSG_WM_DESTROY(OnDestroyPassThru);
+		CHAIN_MSG_MAP(TParent)
+		MESSAGE_HANDLER(WM_LBUTTONDBLCLK,OnLButtonDblClk)
+		MESSAGE_HANDLER(WM_LBUTTONDOWN,OnButtonDown)
+		MESSAGE_HANDLER(WM_RBUTTONDOWN,OnButtonDown)
+		MESSAGE_HANDLER(WM_RBUTTONDBLCLK,OnButtonDown)
+		MESSAGE_HANDLER(WM_RBUTTONUP,OnRButtonUp)
+		MESSAGE_HANDLER(WM_MOUSEMOVE,OnMouseMove)
+		MESSAGE_HANDLER(WM_LBUTTONUP,OnLButtonUp)
+		MESSAGE_HANDLER(WM_KEYDOWN,OnKeyDown);
+		MESSAGE_HANDLER(WM_SYSKEYDOWN,OnKeyDown);
+		MESSAGE_HANDLER(WM_SETFOCUS,OnFocus);
+		MESSAGE_HANDLER(WM_KILLFOCUS,OnFocus);
+		MESSAGE_HANDLER(WM_TIMER,OnTimer);
+		MESSAGE_HANDLER(WM_CAPTURECHANGED,OnCaptureChanged);
+		MESSAGE_HANDLER(WM_GETDLGCODE,OnGetDlgCode);
+		MSG_WM_CHAR(OnChar)
+	END_MSG_MAP()
+
+	virtual void SetFocusItem(t_size index) = 0;
+	virtual t_size GetFocusItem() const = 0;
+	virtual void SetGroupFocusByItem(t_size item) = 0;
+	virtual size_t GetGroupFocus2() const = 0;
+	virtual bool IsItemSelected(t_size index) const = 0;
+	virtual void SetSelection(pfc::bit_array const & affected,pfc::bit_array const & status) = 0;
+	void SelectSingle(size_t which);
+	void SetSelectionAt(size_t idx, bool bSel);
+	virtual bool SelectAll();
+	void SelectNone();
+	virtual void RequestMoveSelection(int delta);
+	bool MoveSelectionProbe(int delta);
+	virtual void RequestReorder( size_t const * order, size_t count ) = 0;
+	virtual void RequestRemoveSelection() = 0;
+	virtual void ExecuteDefaultAction(t_size index) = 0;
+	virtual void ExecuteDefaultActionGroup(t_size base, t_size count) { (void)base; (void)count; }
+	virtual bool ExecuteCanvasDefaultAction(CPoint pt) { (void)pt; return false; }
+
+	virtual t_size GetSelectionStart() const = 0;
+	virtual void SetSelectionStart(t_size val) = 0;
+	//! Full hook for drag-drop loop
+	virtual void RunDragDrop(const CPoint & p_origin,bool p_isRightClick);
+
+	//! Should RunDragDrop() be called at all?
+	virtual bool IsDragDropSupported() {return QueryDragDropTypes() != 0;}
+
+	//! Notification, mandatory to call by SetFocusItem() implementation. \n
+	//! If overridden by subclass, must call parent.
+	virtual void OnFocusChanged(size_t oldFocus, size_t newFocus) { (void)oldFocus; (void)newFocus; }
+	virtual void OnFocusChangedGroup2(size_t baseItem) { (void)baseItem; }
+	//! Notification, mandatory to call by SetSelection() implementation. \n
+	//! If overridden by subclass, must call parent. \n
+	//! Affected: Mask indicating what items ACTUALLY CHANGED, old state to be assumed opposite of new. \n
+	//! During this call, IsSelected() already returns new state.
+	virtual void OnSelectionChanged(pfc::bit_array const& affected, pfc::bit_array const& status) { (void)affected; (void)status; }
+
+	enum {
+		dragDrop_reorder = 1 << 0,
+		dragDrop_external = 1 << 1,
+	};
+
+	virtual uint32_t QueryDragDropTypes() const { return 0; }
+	struct dragDropAccept_t {
+		DWORD dwEFfect = DROPEFFECT_NONE;
+		//! Show drop mark or not?
+		bool showDropMark = false;
+		//! Drop on item or insert into list?
+		bool dropOnItem = false;
+	};
+	//! Deprecated, use DragDropAccept2()
+	virtual DWORD DragDropAccept(IDataObject* obj, bool& showDropMark);
+	//! Return info on what you can do with this IDataObject.
+	virtual dragDropAccept_t DragDropAccept2(IDataObject*);
+	virtual pfc::com_ptr_t<IDataObject> MakeDataObject();
+	//! Called upon drop
+	//! @param pt Drop point in screen coordinates.
+	virtual void OnDrop(IDataObject* obj, CPoint pt) { (void)obj; (void)pt; }
+	virtual DWORD DragDropSourceEffects() { return DROPEFFECT_MOVE | DROPEFFECT_COPY;}
+	virtual void DragDropSourceSucceeded(DWORD effect) { (void)effect; }
+
+	virtual void AdjustSelectionRect(size_t item, CRect& rc) { (void)item; (void)rc; }
+
+	bool GroupFocusActive() const {return GetGroupFocus2() != SIZE_MAX;}
+	
+	void RenderOverlay2(const CRect & p_updaterect,CDCHandle p_dc) override;
+
+	bool IsItemFocused(t_size index) const {return GetFocusItem() == index;}
+	bool IsGroupHeaderFocused2(size_t atItem) const {return GetGroupFocus2() == atItem;}
+	void ToggleSelection(pfc::bit_array const & mask);
+
+	size_t GetSelectedCount(pfc::bit_array const & mask,size_t max = SIZE_MAX) const;
+	size_t GetSelectedCount() const {return GetSelectedCount(pfc::bit_array_true());}
+	size_t GetSingleSel() const;
+	size_t GetFirstSelected() const;
+	size_t GetLastSelected() const;
+
+	//! Execute default action per focus or selection depending on what's focused/selected
+	virtual void ExecuteDefaultActionByFocus();
+
+	void FocusToUpdateRgn(HRGN rgn);
+
+	
+	//! Self-contained minimal drag and drop implementation for reordering list items only; dummy IDataObject presented. \n
+	//! Call from your override of RunDragDrop(), if p_isRightClick is false / left button clicked, never with right button clicked. \n
+	//! On success, use MakeDropReorderPermutation() to fetch the permutation to apply to your content.
+	bool RunReorderDragDrop(CPoint ptOrigin, CPoint & ptDrop);
+
+	bool MakeDropReorderPermutation(pfc::array_t<t_size> & out, CPoint ptDrop) const;
+
+	size_t GetPasteTarget( const CPoint * ptPaste = nullptr ) const;
+
+	//! Fix coordinates of context menu point handed by WM_CONTEXTMENU, turn (-1,-1) into something that makes sense. \n
+	//! Input & output in screen coordinates, per WM_CONTEXTMENU conventions.
+	CPoint GetContextMenuPoint(LPARAM lp);
+	CPoint GetContextMenuPoint(CPoint ptGot);
+	//! Import context menu point coordinates: turn (-1,-1) to something that makes sense; \n
+	//! Returns false if clicked point was outside client area so WM_CONTEXTMENU should be left unhandled.
+	bool GetContextMenuPoint2(CPoint & ptInOut);
+	//! Returns center-of-focused-item point for context menu, in screen coordinates.
+	CPoint GetContextMenuPointDefault();
+
+protected:
+	void ToggleDDScroll(bool p_state);
+	void AbortSelectDragMode() {AbortSelectDragMode(false);}
+	void RenderDropMarkerByOffset2(int offset,CDCHandle p_dc);
+	void RenderDropMarker2(CDCHandle dc, t_size item, bool bInside);
+	bool RenderDropMarkerClipped2(CDCHandle dc, const CRect & update, t_size item, bool bInside);
+	CRect DropMarkerRect(int offset) const;
+	int DropMarkerOffset(t_size marker) const;
+	void AddDropMarkToUpdateRgn(HRGN p_rgn, t_size p_index, bool bInside = false) const;
+	CRect DropMarkerUpdateRect(t_size index,bool bInside) const;
+	bool GetFocusRect(CRect & p_rect);
+	bool GetFocusRectAbs(CRect & p_rect);
+	bool IsOwnDDActive() const {return m_ownDDActive;}
+
+	SIZE DropMarkerMargin() const;
+	void MakeDropMarkerPen(CPen & out) const;
+
+	void EnsureVisibleRectAbs(const CRect & p_rect) override;
+	virtual size_t EvalTypeFind();
+
+	virtual bool AllowRangeSelect() const { return true; }
+
+	size_t GetDropMark( ) const { return m_dropMark; }
+	bool IsDropMarkInside( ) const { return m_dropMarkInside; }
+	void SetDropMark( size_t idx, bool bInside );
+	void ClearDropMark() { SetDropMark(SIZE_MAX, false); }
+private:
+	int OnCreatePassThru(LPCREATESTRUCT lpCreateStruct);
+	void OnDestroyPassThru();
+
+	struct TDDScrollControl {
+		bool m_timerActive = false;
+
+		enum {KTimerID = 0x35bb25af,KTimerPeriod = 25};
+	};
+
+	static constexpr unsigned 
+		KSelectionTimerID = 0xad8abd04,
+		KSelectionTimerPeriod = 50;
+
+	LRESULT OnFocus(UINT,WPARAM,LPARAM,BOOL&);
+	LRESULT OnKeyDown(UINT,WPARAM,LPARAM,BOOL&);
+	LRESULT OnLButtonDblClk(UINT,WPARAM,LPARAM,BOOL&);
+	LRESULT OnButtonDown(UINT,WPARAM,LPARAM,BOOL&);
+	LRESULT OnRButtonUp(UINT,WPARAM,LPARAM,BOOL&);
+	LRESULT OnMouseMove(UINT,WPARAM,LPARAM,BOOL&);
+	LRESULT OnLButtonUp(UINT,WPARAM,LPARAM,BOOL&);
+	LRESULT OnTimer(UINT,WPARAM,LPARAM,BOOL&);
+	LRESULT OnCaptureChanged(UINT,WPARAM,LPARAM,BOOL&);
+	LRESULT OnGetDlgCode(UINT,WPARAM,LPARAM,BOOL&);
+	void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags);
+	void RunTypeFind();
+
+	void OnKeyDown_HomeEndHelper(bool isEnd, int p_keys);
+	void OnKeyDown_SetIndexHelper(int p_index, int p_keys);
+	void OnKeyDown_SetIndexDeltaHelper(int p_delta, int p_keys);
+	void OnKeyDown_SetIndexDeltaLineHelper(int p_delta, int p_keys);
+	void OnKeyDown_SetIndexDeltaPageHelper(int p_delta, int p_keys);
+	void SelectGroupHelper2(size_t p_groupBase,int p_keys);
+	void HandleDragSel(const CPoint & p_pt);
+	void AbortSelectDragMode(bool p_lostCapture);
+	void InitSelectDragMode(const CPoint & p_pt,bool p_rightClick = false);
+
+	void ToggleRangeSelection(pfc::bit_array const & mask);
+	void ToggleGroupSelection2(size_t p_item);
+
+	void HandleDDScroll();
+
+	void PrepareDragDrop(const CPoint & p_point,bool p_isRightClick);
+	void AbortPrepareDragDropMode(bool p_lostCapture = false);
+
+	bool TypeFindCheck(DWORD ts = GetTickCount()) const;
+
+
+protected:
+	// Spacebar handler
+	void ToggleSelectedItems();
+
+	void RenderItem(t_size p_item,const CRect & p_itemRect,const CRect & p_updateRect,CDCHandle p_dc) override;
+	void RenderGroupHeader2(size_t baseItem,const CRect & p_headerRect,const CRect & p_updateRect,CDCHandle p_dc) override;
+	void RenderSubItemText(t_size item, t_size subItem,const CRect & subItemRect,const CRect & updateRect,CDCHandle dc, bool allowColors) override;
+private:
+	bool m_selectDragMode = false;
+	CPoint m_selectDragOriginAbs, m_selectDragCurrentAbs;
+	bool m_selectDragChanged, m_selectDragMoved;
+	TDDScrollControl m_ddScroll;
+
+	bool m_prepareDragDropMode = false, m_prepareDragDropModeRightClick = false;
+	bool m_noEnsureVisible = false;
+	CPoint m_prepareDragDropOrigin;
+	bool ShouldBeginDrag(CPoint ptRef, CPoint ptNow) const;
+
+	bool m_ownDDActive = false;
+	bool m_drawThemeText = false;
+	pfc::string8 m_typeFind; DWORD m_typeFindTS = 0;
+	
+	size_t m_dropMark = SIZE_MAX; bool m_dropMarkInside = false;
+};
+
+//! CListControlWithSelectionImpl implements virtual methods of CListControlWithSelectionBase,
+//! maintaining focus/selection info for you.
+class CListControlWithSelectionImpl : public CListControlWithSelectionBase {
+public:
+
+	enum { selectionSupportNone = 0, selectionSupportSingle, selectionSupportMulti };
+	unsigned m_selectionSupport = selectionSupportMulti;
+
+	void SetSelectionModeNone() { m_selectionSupport = selectionSupportNone; }
+	void SetSelectionModeSingle() { m_selectionSupport = selectionSupportSingle; }
+	void SetSelectionModeMulti() { m_selectionSupport = selectionSupportMulti; }
+	bool IsSingleSelect() const { return m_selectionSupport == selectionSupportSingle; }
+
+	CListControlWithSelectionImpl() {}
+	void SetFocusItem(t_size index);
+	t_size GetFocusItem() const {return m_groupFocus ? SIZE_MAX : m_focus;}
+	void SetGroupFocusByItem(t_size item) override;
+	size_t GetGroupFocus2() const override;
+	bool IsItemSelected(t_size index) const {return index < m_selection.get_size() ? m_selection[index] : false;}
+	void SetSelection(pfc::bit_array const & affected,pfc::bit_array const & status);
+	virtual bool CanSelectItem(size_t index) const { (void)index; return true; }
+	t_size GetSelectionStart() const {return m_selectionStart;}
+	void SetSelectionStart(t_size val) {m_selectionStart = val;}
+
+	void SelHandleReorder(const t_size * order, t_size count);
+	void SelHandleRemoval(const pfc::bit_array & mask, t_size oldCount);
+	void SelHandleInsertion(t_size base, t_size count, bool select);
+	void SelHandleInsertion(pfc::bit_array const & mask, size_t oldCount, size_t newCount, bool select);
+	void SelHandleReset();
+
+	void ReloadData() override;
+	size_t _DebugGetItemCountSel() const { return m_selection.get_size(); }
+
+	virtual void OnItemsReordered( const size_t* order, size_t count ) override;
+	virtual void OnItemsRemoved( pfc::bit_array const & mask, size_t oldCount ) override;
+	virtual void OnItemsInsertedEx(pfc::bit_array const& mask, size_t oldCount, size_t newCount, bool bSelect) override;
+
+	pfc::bit_array_bittable GetSelectionMask() const; // returns a standalone object holding a copy of the state
+	pfc::bit_array_table GetSelectionMaskRef() const; // returns a TEMPORARY object referencing this list's internal data
+
+	bool SelectAll() override;
+
+	const bool* GetSelectionArray() { RefreshSelectionSize(); return m_selection.get_ptr(); }
+
+protected:
+	
+	bool AllowRangeSelect() const override { return m_selectionSupport == selectionSupportMulti; }
+private:
+	void SetSelectionImpl(pfc::bit_array const & affected,pfc::bit_array const & status);
+	void RefreshSelectionSize();
+	void RefreshSelectionSize(t_size size);
+	pfc::array_t<bool> m_selection;
+	size_t m_focus = SIZE_MAX, m_selectionStart = SIZE_MAX;
+	bool m_groupFocus = false;
+};