comparison foosdk/sdk/foobar2000/foo_sample/ui_element_dialog.cpp @ 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
comparison
equal deleted inserted replaced
0:e9bb126753e7 1:20d02a178406
1 #include "stdafx.h"
2
3 #include "resource.h"
4
5 #include <libPPUI/win32_utility.h>
6 #include <libPPUI/win32_op.h> // WIN32_OP()
7 #include <libPPUI/wtl-pp.h> // CCheckBox
8 #include <helpers/atl-misc.h> // ui_element_impl
9
10 #include <libPPUI/DarkMode.h>
11
12 namespace {
13 // Anonymous namespace : standard practice in fb2k components
14 // Nothing outside should have any reason to see these symbols, and we don't want funny results if another cpp has similarly named classes.
15 // service_factory at the bottom takes care of publishing our class.
16
17
18 // I am Sample Component and this is *MY* GUID.
19 // Replace with your own when reusing code. Component authors with colliding GUIDs will be visited by Urdnot Wrex in person.
20 static const GUID guid_myelem = { 0x78ca1d7, 0x4e3a, 0x41d5, { 0xa5, 0xef, 0x9d, 0x1a, 0xf7, 0xd5, 0x79, 0xd0 } };
21
22 enum {
23 FlagLockMinWidth = 1 << 0,
24 FlagLockMinHeight = 1 << 1,
25 FlagLockMaxWidth = 1 << 2,
26 FlagLockMaxHeight = 1 << 3,
27
28 FlagsDefault = 0
29 };
30 static const struct {
31 int btnID;
32 uint32_t flag;
33 } flagsAndButtons[] = {
34 { IDC_LOCK_MIN_WIDTH, FlagLockMinWidth },
35 { IDC_LOCK_MIN_HEIGHT, FlagLockMinHeight },
36 { IDC_LOCK_MAX_WIDTH, FlagLockMaxWidth },
37 { IDC_LOCK_MAX_HEIGHT, FlagLockMaxHeight },
38 };
39
40 class CDialogUIElem : public CDialogImpl<CDialogUIElem>, public ui_element_instance {
41 public:
42 CDialogUIElem( ui_element_config::ptr cfg, ui_element_instance_callback::ptr cb ) : m_callback(cb), m_flags( parseConfig(cfg) ) {}
43
44 enum { IDD = IDD_UI_ELEMENT };
45
46 BEGIN_MSG_MAP_EX( CDialogUIElem )
47 MSG_WM_INITDIALOG(OnInitDialog)
48 MSG_WM_SIZE(OnSize)
49 COMMAND_CODE_HANDLER_EX(BN_CLICKED, OnButtonClicked)
50 END_MSG_MAP()
51
52 void initialize_window(HWND parent) {WIN32_OP(Create(parent) != NULL);}
53 HWND get_wnd() { return m_hWnd; }
54 void set_configuration(ui_element_config::ptr config) {
55 m_flags = parseConfig( config );
56 if ( m_hWnd != NULL ) {
57 configToUI();
58 }
59 m_callback->on_min_max_info_change();
60 }
61 ui_element_config::ptr get_configuration() {return makeConfig(m_flags);}
62 static GUID g_get_guid() {
63 return guid_myelem;
64 }
65 static void g_get_name(pfc::string_base & out) {out = "Sample Dialog as UI Element";}
66 static ui_element_config::ptr g_get_default_configuration() {
67 return makeConfig( );
68 }
69 static const char * g_get_description() {return "This is a sample UI Element using win32 dialog.";}
70 static GUID g_get_subclass() {return ui_element_subclass_utility;}
71
72 ui_element_min_max_info get_min_max_info() {
73 ui_element_min_max_info ret;
74
75 // Note that we play nicely with separate horizontal & vertical DPI.
76 // Such configurations have not been ever seen in circulation, but nothing stops us from supporting such.
77 CSize DPI = QueryScreenDPIEx( *this );
78
79 if ( DPI.cx <= 0 || DPI.cy <= 0 ) { // sanity
80 DPI = CSize(96, 96);
81 }
82
83 if ( m_flags & FlagLockMinWidth ) {
84 ret.m_min_width = MulDiv( 200, DPI.cx, 96 );
85 }
86 if ( m_flags & FlagLockMinHeight ) {
87 ret.m_min_height = MulDiv( 200, DPI.cy, 96 );
88 }
89 if ( m_flags & FlagLockMaxWidth ) {
90 ret.m_max_width = MulDiv( 400, DPI.cx, 96 );
91 }
92 if ( m_flags & FlagLockMaxHeight ) {
93 ret.m_max_height = MulDiv( 400, DPI.cy, 96 );
94 }
95
96 // Deal with WS_EX_STATICEDGE and alike that we might have picked from host
97 ret.adjustForWindow( *this );
98
99 return ret;
100 }
101
102 void applyDark() {
103 t_ui_color color = 0;
104 if (m_callback->query_color(ui_color_darkmode, color)) {
105 m_dark.SetDark(color == 0);
106 }
107 }
108 void notify(const GUID& p_what, t_size p_param1, const void* p_param2, t_size p_param2size) override {
109 // Colors changed? Check dark mode config
110 if (p_what == ui_element_notify_colors_changed) {
111 applyDark();
112 }
113 }
114 private:
115 static uint32_t parseConfig( ui_element_config::ptr cfg ) {
116 try {
117 ::ui_element_config_parser in ( cfg );
118 uint32_t flags; in >> flags;
119 return flags;
120 } catch(exception_io_data) {
121 // If we got here, someone's feeding us nonsense, fall back to defaults
122 return FlagsDefault;
123 }
124 }
125 static ui_element_config::ptr makeConfig(uint32_t flags = FlagsDefault) {
126 ui_element_config_builder out;
127 out << flags;
128 return out.finish( g_get_guid() );
129 }
130 void configToUI() {
131 for ( unsigned i = 0; i < PFC_TABSIZE( flagsAndButtons ); ++ i ) {
132 auto rec = flagsAndButtons[i];
133 // CCheckBox: WTL-PP class overlaying ToggleCheck(bool) and bool IsChecked() over WTL CButton
134 CCheckBox cb ( GetDlgItem( rec.btnID ) );
135 cb.ToggleCheck( (m_flags & rec.flag ) != 0 );
136 }
137 }
138 void OnButtonClicked(UINT uNotifyCode, int nID, CWindow wndCtl) {
139
140 uint32_t flagToFlip = 0;
141 for ( unsigned i = 0; i < PFC_TABSIZE( flagsAndButtons ); ++ i ) {
142 auto rec = flagsAndButtons[i];
143 if ( rec.btnID == nID ) {
144 flagToFlip = rec.flag;
145 }
146 }
147 if ( flagToFlip != 0 ) {
148 uint32_t newFlags = m_flags;
149 CCheckBox cb ( wndCtl );
150 if (cb.IsChecked()) {
151 newFlags |= flagToFlip;
152 } else {
153 newFlags &= ~flagToFlip;
154 }
155 if ( newFlags != m_flags ) {
156 m_flags = newFlags;
157 m_callback->on_min_max_info_change();
158 }
159 }
160 }
161
162 void OnSize(UINT, CSize s) {
163 auto DPI = QueryScreenDPIEx(*this);
164
165 pfc::string_formatter msg;
166 msg << "Current size: ";
167 if ( DPI.cx > 0 && DPI.cy > 0 ) {
168 msg << MulDiv( s.cx, 96, DPI.cx ) << "x" << MulDiv( s.cy, 96, DPI.cy ) << " units, ";
169 }
170 msg << s.cx << "x" << s.cy << " pixels";
171
172 uSetDlgItemText( *this, IDC_STATIC_SIZE, msg );
173 }
174 BOOL OnInitDialog(CWindow, LPARAM) {
175
176 // First tell m_dark whether we're dark nor not
177 applyDark();
178 // Then initialize it
179 m_dark.AddDialogWithControls(*this);
180 // The above two work in any order, though this way is slightly more efficient
181
182 configToUI();
183 {
184 CRect rc;
185 // WIN32_OP_D() - Debug build only retval check and assert
186 // For stuff that practically never fails
187 WIN32_OP_D( GetClientRect( &rc ) );
188 OnSize( 0, rc.Size() );
189 }
190
191 return FALSE;
192 }
193 const ui_element_instance_callback::ptr m_callback;
194 uint32_t m_flags;
195
196 // No fb2k::CDarkModeHooks.
197 // Politely ask our callback if we should render as dark or not, instead of using global settings.
198 // Though in real life it's all the same in the end.
199 DarkMode::CHooks m_dark;
200 };
201
202
203 static service_factory_single_t< ui_element_impl< CDialogUIElem > > g_CDialogUIElem_factory;
204 }
205