|
1
|
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
|