neoGFX
Cross-platform C++ app/game engine
Loading...
Searching...
No Matches
decorated.hpp
Go to the documentation of this file.
1// decorated.hpp
2/*
3 neogfx C++ App/Game Engine
4 Copyright (c) 2020 Leigh Johnston. All Rights Reserved.
5
6 This program is free software: you can redistribute it and / or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
18*/
19
20#pragma once
21
22#include <neogfx/neogfx.hpp>
23#include <neogfx/app/i_app.hpp>
33
34namespace neogfx
35{
36 enum class decoration : uint32_t
37 {
38 None = 0x00000000,
39 Border = 0x00000001,
40 TitleBar = 0x00010000,
41 Menu = 0x00020000,
42 Toolbar = 0x00040000,
43 Dock = 0x00080000,
44 DockAreas = 0x00100000,
45 StatusBar = 0x00200000
46 };
47
48 enum class decoration_style : uint32_t
49 {
50 None = 0x00000000,
51 Window = 0x00000001,
52 Dialog = 0x00000002,
53 Tool = 0x00000004,
54 Menu = 0x00000008,
55 Dock = 0x00000010,
56 Popup = 0x00000040,
57 Splash = 0x00000080,
58 Resizable = 0x01000000,
59 DockAreas = 0x02000000,
60 Nested = 0x10000000,
66 };
67
68 inline constexpr decoration operator|(decoration aLhs, decoration aRhs)
69 {
70 return static_cast<decoration>(static_cast<uint32_t>(aLhs) | static_cast<uint32_t>(aRhs));
71 }
72
73 inline constexpr decoration operator&(decoration aLhs, decoration aRhs)
74 {
75 return static_cast<decoration>(static_cast<uint32_t>(aLhs) & static_cast<uint32_t>(aRhs));
76 }
77
78 inline constexpr decoration& operator|=(decoration& aLhs, decoration aRhs)
79 {
80 return aLhs = static_cast<decoration>(static_cast<uint32_t>(aLhs) | static_cast<uint32_t>(aRhs));
81 }
82
83 inline constexpr decoration& operator&=(decoration& aLhs, decoration aRhs)
84 {
85 return aLhs = static_cast<decoration>(static_cast<uint32_t>(aLhs) & static_cast<uint32_t>(aRhs));
86 }
87
89 {
90 return static_cast<decoration_style>(~static_cast<uint32_t>(aValue));
91 }
92
94 {
95 return static_cast<decoration_style>(static_cast<uint32_t>(aLhs) | static_cast<uint32_t>(aRhs));
96 }
97
99 {
100 return static_cast<decoration_style>(static_cast<uint32_t>(aLhs) & static_cast<uint32_t>(aRhs));
101 }
102
104 {
105 return aLhs = static_cast<decoration_style>(static_cast<uint32_t>(aLhs) | static_cast<uint32_t>(aRhs));
106 }
107
109 {
110 return aLhs = static_cast<decoration_style>(static_cast<uint32_t>(aLhs) & static_cast<uint32_t>(aRhs));
111 }
112
113 template <typename WidgetType, typename... OtherBases>
114 class decorated : public WidgetType, public virtual i_standard_layout_container, public OtherBases...
115 {
116 meta_object(WidgetType)
117 typedef decorated<WidgetType, OtherBases...> self_type;
118 public:
119 typedef WidgetType widget_type;
120 private:
121 struct tracking
122 {
123 widget_part_e part;
124 point startPosition;
125 size startSize;
126 point trackFrom;
127 };
128 public:
129 template <typename... Args>
130 decorated(neogfx::decoration_style aStyle, Args&&... aArgs) :
131 widget_type{ std::forward<Args>(aArgs)... },
132 iInitialized{ false },
133 iStyle{ aStyle },
134 iDecoration{ default_decoration(aStyle) }
135 {
136 init();
137 }
138 public:
140 {
141 if (resizing_context().has_parent_layout())
144 }
146 {
147 return iStyle;
148 }
150 {
151 auto& self = widget_type::as_widget();
152
153 if (iStyle != aStyle)
154 {
155 iStyle = aStyle;
156 self.update();
157 self.update_layout();
158 }
159 }
161 {
162 return iDecoration;
163 }
164 public:
165 bool has_client_widget() const override
166 {
167 return iClient != nullptr;
168 }
169 const i_widget& client_widget() const override
170 {
171 if (has_client_widget())
172 return *iClient;
173 throw no_client_widget();
174 }
176 {
177 if (has_client_widget())
178 return *iClient;
179 throw no_client_widget();
180 }
181 void set_client(i_widget& aClient) override
182 {
184 }
185 void set_client(i_ref_ptr<i_widget> const& aClient) override
186 {
187 if (!iClientLayout)
188 {
191 else
192 {
193 iClientLayout = make_ref<vertical_layout>();
194 iClientLayout->set_padding(neogfx::padding{});
195 layout_item_index clientLayoutIndex = non_client_layout().count();
197 clientLayoutIndex = non_client_layout().index_of(status_bar_layout());
198 non_client_layout().add_at(clientLayoutIndex, iClientLayout);
199 }
200 }
201 iClientLayout->remove_all();
202 iClient = aClient;
203 if (iClient != nullptr)
204 {
205 iClientLayout->add(iClient);
206 iClient->set_parent(iClientLayout->parent_widget());
207 }
208 }
209 const i_title_bar& title_bar() const override
210 {
211 if (iTitleBar)
212 return *iTitleBar;
213 throw no_title_bar();
214 }
216 {
217 return const_cast<i_title_bar&>(to_const(*this).title_bar());
218 }
219 void set_title_bar(i_title_bar& aTitleBar) override
220 {
222 }
224 {
225 iTitleBar = aTitleBar;
226 if (iTitleBarLayout->find(iTitleBar->as_widget()) == std::nullopt)
227 iTitleBarLayout->add(iTitleBar->as_widget());
228 }
229 const i_status_bar& status_bar() const override
230 {
231 if (iStatusBar)
232 return *iStatusBar;
233 throw no_status_bar();
234 }
236 {
237 return const_cast<i_status_bar&>(to_const(*this).status_bar());
238 }
239 void set_status_bar(i_status_bar& aStatusBar) override
240 {
242 }
244 {
245 iStatusBar = aStatusBar;
246 if (iStatusBarLayout->find(iStatusBar->as_widget()) == std::nullopt)
247 iStatusBarLayout->add(iStatusBar->as_widget());
248 }
249 template <typename... Args>
251 {
253 return create_title_bar<normal_title_bar>(std::forward<Args>(aArgs)...);
254 else
255 return create_title_bar<tool_title_bar>(std::forward<Args>(aArgs)...);
256 }
257 template <typename TitleBar, typename... Args>
259 {
260 set_title_bar(to_abstract(make_ref<TitleBar>(*this, std::forward<Args>(aArgs)...)));
261 return iTitleBar;
262 }
263 template <typename StatusBar, typename... Args>
265 {
266 set_status_bar(to_abstract(make_ref<StatusBar>(*this, std::forward<Args>(aArgs)...)));
267 return iStatusBar;
268 }
269 public:
270 bool is_widget() const override
271 {
272 return true;
273 }
274 const i_widget& as_widget() const override
275 {
276 return *this;
277 }
278 i_widget& as_widget() override
279 {
280 return *this;
281 }
282 public:
283 bool part_active(widget_part aPart) const override
284 {
285 switch (aPart.part)
286 {
300 return true;
301 // todo: the rest
302 default:
303 return false;
304 }
305 }
306 widget_part part(const point& aPosition) const override
307 {
308 auto& self = widget_type::as_widget();
309
310#ifdef NEOGFX_DEBUG
311 if (debug::layoutItem == this)
312 service<debug::logger>() << neolib::logger::severity::Debug << "decorated<>::part(...) --> ";
313#endif // NEOGFX_DEBUG
314 auto result = widget_type::part(aPosition);
315 if (result.part == widget_part::Client || result.part == widget_part::NonClient)
316 {
317 if (iTitleBar != nullptr && self.to_client_coordinates(iTitleBar->to_window_coordinates(iTitleBar->client_rect())).contains(aPosition))
318 result.part = widget_part::TitleBar;
319 else if (iMenuLayout && rect{ iMenuLayout->origin(), iMenuLayout->extents() }.contains(aPosition))
320 result.part = widget_part::Grab;
321 else if (iToolbarLayout && rect{ iToolbarLayout->origin(), iToolbarLayout->extents() }.contains(aPosition))
322 result.part = widget_part::Grab;
324 {
325 enum { left = 1, top = 2, right = 4, bottom = 8 };
326 int hit = 0;
327 auto const nonClientRect = self.to_client_coordinates(self.non_client_rect());
328 auto const nonClientBorder = self.is_root() ? self.root().window_border() : self.internal_spacing();
329 if (aPosition.x < nonClientRect.left() + nonClientBorder.left)
330 hit |= left;
331 if (aPosition.x > nonClientRect.right() - nonClientBorder.right)
332 hit |= right;
333 if (aPosition.y < nonClientRect.top() + nonClientBorder.top)
334 hit |= top;
335 if (aPosition.y > nonClientRect.bottom() - nonClientBorder.bottom)
336 hit |= bottom;
337 if (iStatusBar != nullptr)
338 {
339 point const sizeGripPos = self.to_client_coordinates(iStatusBar->size_grip().non_client_rect().position());
340 rect const sizeGripRect = { sizeGripPos, size{ nonClientRect.bottom_right() - sizeGripPos } };
341 if (sizeGripRect.contains(aPosition))
342 hit |= (right | bottom);
343 }
344 if (hit & top && hit & left)
345 result.part = widget_part::BorderTopLeft;
346 else if (hit & top && hit & right)
347 result.part = widget_part::BorderTopRight;
348 else if (hit & bottom && hit & left)
349 result.part = widget_part::BorderBottomLeft;
350 else if (hit & bottom && hit & right)
351 result.part = widget_part::BorderBottomRight;
352 else if (hit & left)
353 result.part = widget_part::BorderLeft;
354 else if (hit & top)
355 result.part = widget_part::BorderTop;
356 else if (hit & right)
357 result.part = widget_part::BorderRight;
358 else if (hit & bottom)
359 result.part = widget_part::BorderBottom;
360 }
361 }
362#ifdef NEOGFX_DEBUG
363 if (debug::layoutItem == this)
364 service<debug::logger>() << neolib::logger::severity::Debug << result.part << endl;
365#endif // NEOGFX_DEBUG
366 return result;
367 }
368 public:
369 using widget_type::has_layout;
370 using widget_type::layout;
371 bool has_layout(standard_layout aStandardLayout) const override
372 {
373 auto& self = widget_type::as_widget();
374
375 switch (aStandardLayout)
376 {
378 return self.has_layout();
380 return !!iClientLayout;
382 return !!iNonClientLayout;
384 return !!iTitleBarLayout;
386 return !!iMenuLayout;
388 return !!iToolbarLayout;
390 return !!iDockLayout;
392 return !!iStatusBarLayout;
393 default:
394 return false;
395 }
396 }
397 const i_layout& layout(standard_layout aStandardLayout, layout_position aPosition = layout_position::None) const override
398 {
399 auto& self = widget_type::as_widget();
400
401 if (!has_layout(aStandardLayout))
403
404 switch (aStandardLayout)
405 {
407 return self.layout();
409 return has_client_widget() ? client_widget().layout() : *iClientLayout;
411 return *iNonClientLayout;
413 return *iTitleBarLayout;
415 return *iMenuLayout;
417 if (aPosition == layout_position::None)
418 return *iToolbarLayout;
419 else
420 return iToolbarLayout->part(aPosition);
422 if (aPosition == layout_position::None)
423 return *iDockLayout;
424 else
425 return iDockLayout->part(aPosition);
427 return *iStatusBarLayout;
428 default:
430 }
431 }
433 {
434 return const_cast<i_layout&>(to_const(*this).layout(aStandardLayout, aPosition));
435 }
436 public:
437 void fix_weightings(bool aRecalculate = true) override
438 {
439 widget_type::fix_weightings(aRecalculate);
440 if (resizing_context().has_parent_layout())
441 {
444 }
445 }
446 protected:
447 neogfx::size_policy size_policy() const override
448 {
449 auto& self = widget_type::as_widget();
450
451 if (self.has_size_policy() || self.is_root())
452 return widget_type::size_policy();
454 }
455 size weight() const override
456 {
457 auto& self = widget_type::as_widget();
458
459 if (self.has_weight() || self.is_root())
460 return widget_type::weight();
461 return size{ 1.0 };
462 }
463 protected:
464 void capture_released() override
465 {
466 widget_type::capture_released();
467 iTracking = std::nullopt;
468 }
469 protected:
470 void mouse_button_pressed(mouse_button aButton, const point& aPosition, key_modifiers_e aKeyModifiers) override
471 {
472 auto& self = widget_type::as_widget();
473
474 widget_type::mouse_button_pressed(aButton, aPosition, aKeyModifiers);
475 if (aButton == mouse_button::Left && self.capturing() &&
476 (!self.is_root() || self.root().is_nested()))
477 {
478 auto const clickedPart = part(aPosition);
479 if (part_active(clickedPart))
480 {
481 iTracking = tracking{ clickedPart.part, resizing_context().position(), resizing_context().extents(), widget_type::to_window_coordinates(aPosition) };
482 if (self.has_root())
483 self.root().window_manager().update_mouse_cursor(self.root());
484 }
485 }
486 }
487 void mouse_moved(const point& aPosition, key_modifiers_e aKeyModifiers) override
488 {
489 widget_type::mouse_moved(aPosition, aKeyModifiers);
490 update_tracking(aPosition);
491 }
492 protected:
493 rect default_clip_rect(bool aIncludeNonClient = false) const override
494 {
495 auto clipRect = widget_type::default_clip_rect(aIncludeNonClient);
496 if (!aIncludeNonClient && has_layout(standard_layout::TitleBar) &&
497 iTitleBar && (iTitleBar->as_widget().widget_type() & neogfx::widget_type::NonClient) == neogfx::widget_type::NonClient)
499 return clipRect;
500 }
501 protected:
502 void init()
503 {
504 auto& self = widget_type::as_widget();
505
506 if (iInitialized)
507 return;
508
510 return; // surface not yet created
511
512 self.set_padding(neogfx::padding{});
513
514 iNonClientLayout.emplace(*this);
515 non_client_layout().set_padding(neogfx::padding{});
517
519 {
520 iTitleBarLayout.emplace(non_client_layout());
521 iTitleBarLayout->set_padding(neogfx::padding{});
522 }
523 // todo: create widgets for the following decorations
525 {
526 iMenuLayout.emplace(non_client_layout());
527 iMenuLayout->set_padding(neogfx::padding{});
528 }
530 {
531 iToolbarLayout.emplace(non_client_layout());
532 iToolbarLayout->set_padding(neogfx::padding{});
533 }
535 {
536 if (!iDockLayoutContainer)
537 {
538 iDockLayoutContainer.emplace(has_layout(standard_layout::Toolbar) ?
540 iDockLayoutContainer->set_padding(neogfx::padding{});
541 iDockLayoutContainer->painted([&](i_graphics_context& aGc)
542 {
543 service<i_skin_manager>().active_skin().draw_separators(aGc, *iDockLayoutContainer, dock_layout(dock_area::East).parent_layout());
544 service<i_skin_manager>().active_skin().draw_separators(aGc, *iDockLayoutContainer, dock_layout(dock_area::North).parent_layout());
545 });
546 }
547 iDockLayout.emplace(*iDockLayoutContainer);
548 iDockLayout->set_padding(neogfx::padding{});
549 iDockLayout->center().set_padding(service<i_app>().current_style().padding(padding_role::Dock));
550 }
552 {
553 iStatusBarLayout.emplace(non_client_layout());
554 iStatusBarLayout->set_padding(neogfx::padding{});
555 }
556
557 iInitialized = true;
558 }
560 {
561 auto& self = widget_type::as_widget();
562
564 static_cast<i_layout_item const&>(self.parent_layout()) : static_cast<i_layout_item const&>(self);
565 }
567 {
568 return const_cast<i_layout_item&>(to_const(*this).resizing_context());
569 }
570 void update_tracking(const point& aPosition)
571 {
572 auto& self = widget_type::as_widget();
573
574 if (iTracking)
575 {
576 i_layout_item& resizingContext = resizing_context();
577 auto const delta = widget_type::to_window_coordinates(aPosition) - iTracking->trackFrom;
578 auto const currentPosition = resizingContext.position();
579 auto const currentSize = resizingContext.extents();
580 optional_point newPosition;
581 optional_size newSize;
582 switch (iTracking->part)
583 {
585 newPosition = iTracking->startPosition + delta;
586 break;
588 newSize = resizingContext.minimum_size().max(size{ iTracking->startSize.cx - delta.x, iTracking->startSize.cy });
589 if (newSize != currentSize)
590 newPosition = iTracking->startPosition - (*newSize - iTracking->startSize).with_cy(0.0);
591 break;
593 newSize = resizingContext.minimum_size().max(size{ iTracking->startSize.cx - delta.x, iTracking->startSize.cy - delta.y });
594 if (newSize != currentSize)
595 newPosition = iTracking->startPosition - (*newSize - iTracking->startSize);
596 break;
598 newSize = resizingContext.minimum_size().max(size{ iTracking->startSize.cx, iTracking->startSize.cy - delta.y });
599 if (newSize != currentSize)
600 newPosition = iTracking->startPosition - (*newSize - iTracking->startSize).with_cx(0.0);
601 break;
603 newSize = resizingContext.minimum_size().max(size{ iTracking->startSize.cx + delta.x, iTracking->startSize.cy - delta.y });
604 if (newSize != currentSize)
605 newPosition = iTracking->startPosition - (*newSize - iTracking->startSize).with_cx(0.0);
606 break;
608 newSize = resizingContext.minimum_size().max(size{ iTracking->startSize.cx + delta.x, iTracking->startSize.cy });
609 break;
611 newSize = resizingContext.minimum_size().max(size{ iTracking->startSize.cx + delta.x, iTracking->startSize.cy + delta.y });
612 break;
614 newSize = resizingContext.minimum_size().max(size{ iTracking->startSize.cx, iTracking->startSize.cy + delta.y });
615 break;
617 newSize = resizingContext.minimum_size().max(size{ iTracking->startSize.cx - delta.x, iTracking->startSize.cy + delta.y });
618 if (newSize != currentSize)
619 newPosition = iTracking->startPosition - (*newSize - iTracking->startSize).with_cy(0.0);
620 break;
621 }
622 if (newPosition && newPosition != currentPosition)
623 {
624#ifdef NEOGFX_DEBUG
625 if (debug::layoutItem == this)
626 service<debug::logger>() << neolib::logger::severity::Debug << "update_tracking(" << aPosition << "): " << currentPosition << " -> " << newPosition << endl;
627#endif // NEOGFX_DEBUG
629 {
630 // todo - undock/float
631 }
632 else
633 self.move(newPosition.value());
634 }
635 if (newSize && newSize != currentSize)
636 {
637#ifdef NEOGFX_DEBUG
638 if (debug::layoutItem == this)
639 service<debug::logger>() << neolib::logger::severity::Debug << "update_tracking(" << aPosition << "): " << currentSize << " -> " << newSize << endl;
640#endif // NEOGFX_DEBUG
642 {
643 resizingContext.set_fixed_size(newSize, false);
645 }
646 else
647 self.resize(newSize.value());
648 }
649 }
650 }
651 private:
652 static neogfx::decoration default_decoration(neogfx::decoration_style aStyle)
653 {
654 auto result = neogfx::decoration::None;
660 result |= neogfx::decoration::Menu;
662 result |= neogfx::decoration::Dock;
667 return result;
668 }
669 private:
670 bool iInitialized;
672 neogfx::decoration iDecoration;
673 std::optional<vertical_layout> iNonClientLayout;
674 std::optional<vertical_layout> iTitleBarLayout;
675 std::optional<vertical_layout> iMenuLayout;
676 std::optional<border_layout> iToolbarLayout;
677 std::optional<layout_manager<scrollable_widget<>>> iDockLayoutContainer;
678 std::optional<border_layout> iDockLayout;
679 std::optional<vertical_layout> iStatusBarLayout;
680 ref_ptr<i_title_bar> iTitleBar;
681 ref_ptr<i_status_bar> iStatusBar;
682 ref_ptr<i_layout> iClientLayout;
683 ref_ptr<i_widget> iClient;
684 std::optional<tracking> iTracking;
685 };
686}
coordinate_type x
coordinate_type y
bool contains(const point_type &point) const
dimension_type cy
void set_client(i_widget &aClient) override
neogfx::decoration decoration() const
bool has_layout(standard_layout aStandardLayout) const override
neogfx::decoration_style decoration_style() const
rect default_clip_rect(bool aIncludeNonClient=false) const override
bool part_active(widget_part aPart) const override
size weight() const override
void set_client(i_ref_ptr< i_widget > const &aClient) override
void mouse_moved(const point &aPosition, key_modifiers_e aKeyModifiers) override
i_layout & layout(standard_layout aStandardLayout, layout_position aPosition=layout_position::None) override
const i_widget & client_widget() const override
void update_tracking(const point &aPosition)
const i_layout & layout(standard_layout aStandardLayout, layout_position aPosition=layout_position::None) const override
widget_part part(const point &aPosition) const override
const i_title_bar & title_bar() const override
void set_status_bar(i_status_bar &aStatusBar) override
neogfx::autoscale autoscale() const
i_widget & client_widget() override
WidgetType widget_type
void set_title_bar(i_ref_ptr< i_title_bar > const &aTitleBar)
void set_decoration_style(neogfx::decoration_style aStyle)
i_status_bar & status_bar() override
void set_status_bar(i_ref_ptr< i_status_bar > const &aStatusBar)
ref_ptr< i_title_bar > create_title_bar(Args &&... aArgs)
ref_ptr< i_status_bar > create_status_bar(Args &&... aArgs)
i_widget & as_widget() override
i_layout_item const & resizing_context() const
decorated(neogfx::decoration_style aStyle, Args &&... aArgs)
ref_ptr< i_title_bar > create_title_bar(Args &&... aArgs)
i_title_bar & title_bar() override
i_layout_item & resizing_context()
const i_status_bar & status_bar() const override
void mouse_button_pressed(mouse_button aButton, const point &aPosition, key_modifiers_e aKeyModifiers) override
neogfx::size_policy size_policy() const override
void capture_released() override
const i_widget & as_widget() const override
void set_title_bar(i_title_bar &aTitleBar) override
bool is_widget() const override
void fix_weightings(bool aRecalculate=true) override
bool has_client_widget() const override
virtual const i_layout & parent_layout() const =0
virtual void fix_weightings(bool aRecalculate=true)=0
virtual void update_layout(bool aDeferLayout=true, bool aAncestors=false)=0
virtual neogfx::autoscale autoscale() const =0
virtual void set_spacing(optional_size const &sSpacing, bool aUpdateLayout=true)=0
virtual layout_item_index count() const =0
virtual layout_item_index index_of(const i_layout_item &aItem) const =0
virtual i_layout_item & add_at(layout_item_index aPosition, i_layout_item &aItem)=0
const i_layout & non_client_layout() const
Definition i_layout.hpp:94
const i_layout & status_bar_layout() const
Definition i_layout.hpp:158
const i_layout & dock_layout(layout_position aPosition=layout_position::Left) const
Definition i_layout.hpp:126
const i_layout & toolbar_layout(layout_position aPosition=layout_position::Top) const
Definition i_layout.hpp:118
virtual const i_layout & layout() const =0
size extents() const final
reference value() final
Definition optional.hpp:118
constexpr font_style & operator&=(font_style &aLhs, font_style aRhs)
Definition font.hpp:81
constexpr style_aspect operator&(style_aspect aLhs, style_aspect aRhs)
Definition i_style.hpp:60
audio_channel operator~(audio_channel lhs)
basic_delta< coordinate > delta
uint32_t layout_item_index
Definition i_layout.hpp:35
decoration_style
Definition decorated.hpp:49
constexpr font_style & operator|=(font_style &aLhs, font_style aRhs)
Definition font.hpp:76
standard_layout
Definition i_layout.hpp:49
mouse_button
Definition i_mouse.hpp:31
constexpr style_aspect operator|(style_aspect aLhs, style_aspect aRhs)
Definition i_style.hpp:55
layout_position
Definition i_layout.hpp:39
Definition plf_hive.h:79
#define meta_object(...)
Definition i_object.hpp:28
enum neogfx::widget_part::widget_part_e part