Update git submodules
[LibreOffice.git] / vcl / win / gdi / salnativewidgets-luna.cxx
blob318009acadaef4c5ab42b689077b44a70cebb1f4
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 // General info:
21 // http://msdn.microsoft.com/en-us/library/windows/desktop/hh270423%28v=vs.85%29.aspx
22 // http://msdn.microsoft.com/en-us/library/windows/desktop/bb773178%28v=vs.85%29.aspx
24 // Useful tool to explore the themes & their rendering:
25 // http://privat.rejbrand.se/UxExplore.exe
26 // (found at http://stackoverflow.com/questions/4009701/windows-visual-themes-gallery-of-parts-and-states/4009712#4009712)
28 // Theme subclasses:
29 // http://msdn.microsoft.com/en-us/library/windows/desktop/bb773218%28v=vs.85%29.aspx
31 // Drawing in non-client area (general DWM-related info):
32 // http://msdn.microsoft.com/en-us/library/windows/desktop/bb688195%28v=vs.85%29.aspx
34 #include <rtl/ustring.h>
36 #include <osl/diagnose.h>
37 #include <osl/module.h>
38 #include <o3tl/char16_t2wchar_t.hxx>
40 #include <vcl/svapp.hxx>
41 #include <vcl/settings.hxx>
42 #include <toolbarvalue.hxx>
43 #include <menubarvalue.hxx>
45 #include <win/svsys.h>
46 #include <win/salgdi.h>
47 #include <win/saldata.hxx>
48 #include <win/salframe.h>
49 #include <win/scoped_gdi.hxx>
50 #include <win/wingdiimpl.hxx>
52 #include <uxtheme.h>
53 #include <vssym32.h>
55 #include <map>
56 #include <string>
57 #include <optional>
58 #include <ControlCacheKey.hxx>
60 typedef std::map< std::wstring, HTHEME > ThemeMap;
61 static ThemeMap aThemeMap;
63 /*********************************************************
64 * Initialize XP theming and local stuff
65 *********************************************************/
66 void SalData::initNWF()
68 ImplSVData* pSVData = ImplGetSVData();
70 // the menu bar and the top docking area should have a common background (gradient)
71 pSVData->maNWFData.mbMenuBarDockingAreaCommonBG = true;
74 // *********************************************************
75 // * Release theming handles
76 // ********************************************************
77 void SalData::deInitNWF()
79 for( auto& rEntry : aThemeMap )
80 CloseThemeData(rEntry.second);
81 aThemeMap.clear();
84 static HTHEME getThemeHandle(HWND hWnd, LPCWSTR name, WinSalGraphicsImplBase* pGraphicsImpl)
86 if( GetSalData()->mbThemeChanged )
88 // throw away invalid theme handles
89 SalData::deInitNWF();
90 // throw away native control cache
91 pGraphicsImpl->ClearNativeControlCache();
92 GetSalData()->mbThemeChanged = false;
95 ThemeMap::iterator iter;
96 if( (iter = aThemeMap.find( name )) != aThemeMap.end() )
97 return iter->second;
98 // theme not found -> add it to map
99 HTHEME hTheme = OpenThemeData( hWnd, name );
100 if( hTheme != nullptr )
101 aThemeMap[name] = hTheme;
102 return hTheme;
105 bool WinSalGraphics::isNativeControlSupported( ControlType nType, ControlPart nPart )
107 HTHEME hTheme = nullptr;
109 switch( nType )
111 case ControlType::Pushbutton:
112 case ControlType::Radiobutton:
113 case ControlType::Checkbox:
114 if( nPart == ControlPart::Entire )
115 hTheme = getThemeHandle(mhWnd, L"Button", mWinSalGraphicsImplBase);
116 break;
117 case ControlType::Scrollbar:
118 if( nPart == ControlPart::DrawBackgroundHorz || nPart == ControlPart::DrawBackgroundVert )
119 return false; // no background painting needed
120 if( nPart == ControlPart::Entire )
121 hTheme = getThemeHandle(mhWnd, L"Scrollbar", mWinSalGraphicsImplBase);
122 break;
123 case ControlType::Combobox:
124 if( nPart == ControlPart::HasBackgroundTexture )
125 return false; // we do not paint the inner part (ie the selection background/focus indication)
126 if( nPart == ControlPart::Entire )
127 hTheme = getThemeHandle(mhWnd, L"Edit", mWinSalGraphicsImplBase);
128 else if( nPart == ControlPart::ButtonDown )
129 hTheme = getThemeHandle(mhWnd, L"Combobox", mWinSalGraphicsImplBase);
130 break;
131 case ControlType::Spinbox:
132 if( nPart == ControlPart::Entire )
133 hTheme = getThemeHandle(mhWnd, L"Edit", mWinSalGraphicsImplBase);
134 else if( nPart == ControlPart::AllButtons ||
135 nPart == ControlPart::ButtonUp || nPart == ControlPart::ButtonDown ||
136 nPart == ControlPart::ButtonLeft|| nPart == ControlPart::ButtonRight )
137 hTheme = getThemeHandle(mhWnd, L"Spin", mWinSalGraphicsImplBase);
138 break;
139 case ControlType::SpinButtons:
140 if( nPart == ControlPart::Entire || nPart == ControlPart::AllButtons )
141 hTheme = getThemeHandle(mhWnd, L"Spin", mWinSalGraphicsImplBase);
142 break;
143 case ControlType::Editbox:
144 case ControlType::MultilineEditbox:
145 if( nPart == ControlPart::HasBackgroundTexture )
146 return false; // we do not paint the inner part (ie the selection background/focus indication)
147 //return TRUE;
148 if( nPart == ControlPart::Entire )
149 hTheme = getThemeHandle(mhWnd, L"Edit", mWinSalGraphicsImplBase);
150 break;
151 case ControlType::Listbox:
152 if( nPart == ControlPart::HasBackgroundTexture )
153 return false; // we do not paint the inner part (ie the selection background/focus indication)
154 if( nPart == ControlPart::Entire || nPart == ControlPart::ListboxWindow )
155 hTheme = getThemeHandle(mhWnd, L"Listview", mWinSalGraphicsImplBase);
156 else if( nPart == ControlPart::ButtonDown )
157 hTheme = getThemeHandle(mhWnd, L"Combobox", mWinSalGraphicsImplBase);
158 break;
159 case ControlType::TabPane:
160 case ControlType::TabBody:
161 case ControlType::TabItem:
162 if( nPart == ControlPart::Entire )
163 hTheme = getThemeHandle(mhWnd, L"Tab", mWinSalGraphicsImplBase);
164 break;
165 case ControlType::Toolbar:
166 if( nPart == ControlPart::Entire || nPart == ControlPart::Button )
167 hTheme = getThemeHandle(mhWnd, L"Toolbar", mWinSalGraphicsImplBase);
168 else
169 // use rebar theme for grip and background
170 hTheme = getThemeHandle(mhWnd, L"Rebar", mWinSalGraphicsImplBase);
171 break;
172 case ControlType::Menubar:
173 if( nPart == ControlPart::Entire )
174 hTheme = getThemeHandle(mhWnd, L"Rebar", mWinSalGraphicsImplBase);
175 else if( GetSalData()->mbThemeMenuSupport )
177 if( nPart == ControlPart::MenuItem )
178 hTheme = getThemeHandle(mhWnd, L"Menu", mWinSalGraphicsImplBase);
180 break;
181 case ControlType::MenuPopup:
182 if( GetSalData()->mbThemeMenuSupport )
184 if( nPart == ControlPart::Entire ||
185 nPart == ControlPart::MenuItem ||
186 nPart == ControlPart::MenuItemCheckMark ||
187 nPart == ControlPart::MenuItemRadioMark ||
188 nPart == ControlPart::Separator )
189 hTheme = getThemeHandle(mhWnd, L"Menu", mWinSalGraphicsImplBase);
191 break;
192 case ControlType::Progress:
193 case ControlType::LevelBar:
194 if( nPart == ControlPart::Entire )
195 hTheme = getThemeHandle(mhWnd, L"Progress", mWinSalGraphicsImplBase);
196 break;
197 case ControlType::Slider:
198 if( nPart == ControlPart::TrackHorzArea || nPart == ControlPart::TrackVertArea )
199 hTheme = getThemeHandle(mhWnd, L"Trackbar", mWinSalGraphicsImplBase);
200 break;
201 case ControlType::ListNode:
202 if( nPart == ControlPart::Entire )
203 hTheme = getThemeHandle(mhWnd, L"TreeView", mWinSalGraphicsImplBase);
204 break;
205 default:
206 hTheme = nullptr;
207 break;
210 return (hTheme != nullptr);
213 bool WinSalGraphics::hitTestNativeControl( ControlType,
214 ControlPart,
215 const tools::Rectangle&,
216 const Point&,
217 bool& )
219 return false;
222 static bool ImplDrawTheme( HTHEME hTheme, HDC hDC, int iPart, int iState, RECT rc, const OUString& aStr)
224 HRESULT hr = DrawThemeBackground( hTheme, hDC, iPart, iState, &rc, nullptr);
226 if( aStr.getLength() )
228 RECT rcContent;
229 hr = GetThemeBackgroundContentRect( hTheme, hDC, iPart, iState, &rc, &rcContent);
230 hr = DrawThemeText( hTheme, hDC, iPart, iState,
231 o3tl::toW(aStr.getStr()), -1,
232 DT_CENTER | DT_VCENTER | DT_SINGLELINE,
233 0, &rcContent);
235 return (hr == S_OK);
238 // TS_TRUE returns optimal size
239 static std::optional<Size> ImplGetThemeSize(HTHEME hTheme, HDC hDC, int iPart, int iState, LPCRECT pRect, THEMESIZE eTS = TS_TRUE)
241 if (SIZE aSz; SUCCEEDED(GetThemePartSize(hTheme, hDC, iPart, iState, pRect, eTS, &aSz)))
242 return Size(aSz.cx, aSz.cy);
243 return {};
246 static tools::Rectangle ImplGetThemeRect( HTHEME hTheme, HDC hDC, int iPart, int iState, const tools::Rectangle& /* aRect */, THEMESIZE eTS = TS_TRUE )
248 if (const std::optional<Size> oSz = ImplGetThemeSize(hTheme, hDC, iPart, iState, nullptr, eTS))
249 return tools::Rectangle( 0, 0, oSz->Width(), oSz->Height() );
250 else
251 return tools::Rectangle();
254 // Helper functions
256 static void ImplConvertSpinbuttonValues( ControlPart nControlPart, const ControlState& rState, const tools::Rectangle& rRect,
257 int* pLunaPart, int *pLunaState, RECT *pRect )
259 if( nControlPart == ControlPart::ButtonDown )
261 *pLunaPart = SPNP_DOWN;
262 if( rState & ControlState::PRESSED )
263 *pLunaState = DNS_PRESSED;
264 else if( !(rState & ControlState::ENABLED) )
265 *pLunaState = DNS_DISABLED;
266 else if( rState & ControlState::ROLLOVER )
267 *pLunaState = DNS_HOT;
268 else
269 *pLunaState = DNS_NORMAL;
271 if( nControlPart == ControlPart::ButtonUp )
273 *pLunaPart = SPNP_UP;
274 if( rState & ControlState::PRESSED )
275 *pLunaState = UPS_PRESSED;
276 else if( !(rState & ControlState::ENABLED) )
277 *pLunaState = UPS_DISABLED;
278 else if( rState & ControlState::ROLLOVER )
279 *pLunaState = UPS_HOT;
280 else
281 *pLunaState = UPS_NORMAL;
283 if( nControlPart == ControlPart::ButtonRight )
285 *pLunaPart = SPNP_UPHORZ;
286 if( rState & ControlState::PRESSED )
287 *pLunaState = DNHZS_PRESSED;
288 else if( !(rState & ControlState::ENABLED) )
289 *pLunaState = DNHZS_DISABLED;
290 else if( rState & ControlState::ROLLOVER )
291 *pLunaState = DNHZS_HOT;
292 else
293 *pLunaState = DNHZS_NORMAL;
295 if( nControlPart == ControlPart::ButtonLeft )
297 *pLunaPart = SPNP_DOWNHORZ;
298 if( rState & ControlState::PRESSED )
299 *pLunaState = UPHZS_PRESSED;
300 else if( !(rState & ControlState::ENABLED) )
301 *pLunaState = UPHZS_DISABLED;
302 else if( rState & ControlState::ROLLOVER )
303 *pLunaState = UPHZS_HOT;
304 else
305 *pLunaState = UPHZS_NORMAL;
308 pRect->left = rRect.Left();
309 pRect->right = rRect.Right()+1;
310 pRect->top = rRect.Top();
311 pRect->bottom = rRect.Bottom()+1;
314 /// Draw an own toolbar style on Windows Vista or later, looks better there
315 static void impl_drawAeroToolbar( HDC hDC, RECT rc, bool bHorizontal )
317 if ( rc.top == 0 && bHorizontal )
319 const int GRADIENT_HEIGHT = 32;
321 LONG gradient_break = rc.top;
322 LONG gradient_bottom = rc.bottom - 1;
323 GRADIENT_RECT g_rect[1] = { { 0, 1 } };
325 // very slow gradient at the top (if we have space for that)
326 if ( gradient_bottom - rc.top > GRADIENT_HEIGHT )
328 gradient_break = gradient_bottom - GRADIENT_HEIGHT;
330 TRIVERTEX vert[2] = {
331 { rc.left, rc.top, 0xff00, 0xff00, 0xff00, 0xff00 },
332 { rc.right, gradient_break, 0xfa00, 0xfa00, 0xfa00, 0xff00 },
334 GdiGradientFill( hDC, vert, 2, g_rect, 1, GRADIENT_FILL_RECT_V );
337 // gradient at the bottom
338 TRIVERTEX vert[2] = {
339 { rc.left, gradient_break, 0xfa00, 0xfa00, 0xfa00, 0xff00 },
340 { rc.right, gradient_bottom, 0xf000, 0xf000, 0xf000, 0xff00 }
342 GdiGradientFill( hDC, vert, 2, g_rect, 1, GRADIENT_FILL_RECT_V );
344 // and a darker horizontal line under that
345 ScopedSelectedHPEN hPen(hDC, CreatePen(PS_SOLID, 1, RGB( 0xb0, 0xb0, 0xb0)));
347 MoveToEx( hDC, rc.left, gradient_bottom, nullptr );
348 LineTo( hDC, rc.right, gradient_bottom );
350 else
352 ScopedHBRUSH hbrush(CreateSolidBrush(RGB(0xf0, 0xf0, 0xf0)));
353 FillRect(hDC, &rc, hbrush.get());
355 // darker line to distinguish the toolbar and viewshell
356 // it is drawn only for the horizontal toolbars; it did not look well
357 // when done for the vertical ones too
358 if ( bHorizontal )
360 LONG from_x, from_y, to_x, to_y;
362 from_x = rc.left;
363 to_x = rc.right;
364 from_y = to_y = rc.top;
366 ScopedSelectedHPEN hPen(hDC, CreatePen(PS_SOLID, 1, RGB( 0xb0, 0xb0, 0xb0)));
368 MoveToEx( hDC, from_x, from_y, nullptr );
369 LineTo( hDC, to_x, to_y );
374 static bool implDrawNativeMenuMark(HDC hDC, HTHEME hTheme, RECT rc, ControlPart nPart,
375 ControlState nState, OUString const& aCaption)
377 int iState = (nState & ControlState::ENABLED) ? MCB_NORMAL : MCB_DISABLED;
378 ImplDrawTheme(hTheme, hDC, MENU_POPUPCHECKBACKGROUND, iState, rc, aCaption);
379 if (nPart == ControlPart::MenuItemCheckMark)
380 iState = (nState & ControlState::ENABLED) ? MC_CHECKMARKNORMAL : MC_CHECKMARKDISABLED;
381 else
382 iState = (nState & ControlState::ENABLED) ? MC_BULLETNORMAL : MC_BULLETDISABLED;
383 // tdf#133697: Get true size of mark, to avoid stretching
384 if (auto oSize = ImplGetThemeSize(hTheme, hDC, MENU_POPUPCHECK, iState, &rc))
386 // center the mark inside the passed rectangle
387 if (const auto dx = (rc.right - rc.left - oSize->Width() + 1) / 2; dx > 0)
389 rc.left += dx;
390 rc.right = rc.left + oSize->Width();
392 if (const auto dy = (rc.bottom - rc.top - oSize->Height() + 1) / 2; dy > 0)
394 rc.top += dy;
395 rc.bottom = rc.top + oSize->Height();
398 return ImplDrawTheme(hTheme, hDC, MENU_POPUPCHECK, iState, rc, aCaption);
401 bool UseDarkMode()
403 static bool bOSSupportsDarkMode = OSSupportsDarkMode();
404 if (!bOSSupportsDarkMode)
405 return false;
407 bool bRet(false);
408 switch (MiscSettings::GetDarkMode())
410 case 0: // auto
411 default:
413 HINSTANCE hUxthemeLib = LoadLibraryExW(L"uxtheme.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
414 if (!hUxthemeLib)
415 return false;
417 typedef bool(WINAPI* ShouldAppsUseDarkMode_t)();
418 if (auto ShouldAppsUseDarkMode = reinterpret_cast<ShouldAppsUseDarkMode_t>(GetProcAddress(hUxthemeLib, MAKEINTRESOURCEA(132))))
419 bRet = ShouldAppsUseDarkMode();
421 FreeLibrary(hUxthemeLib);
422 break;
424 case 1: // light
425 bRet = false;
426 break;
427 case 2: // dark
428 bRet = true;
429 break;
432 return bRet;
435 static bool ImplDrawNativeControl( HDC hDC, HTHEME hTheme, RECT rc,
436 ControlType nType,
437 ControlPart nPart,
438 ControlState nState,
439 const ImplControlValue& aValue,
440 OUString const & aCaption,
441 bool bUseDarkMode )
443 // a listbox dropdown is actually a combobox dropdown
444 if( nType == ControlType::Listbox )
445 if( nPart == ControlPart::ButtonDown )
446 nType = ControlType::Combobox;
448 // draw entire combobox as a large edit box
449 if( nType == ControlType::Combobox )
450 if( nPart == ControlPart::Entire )
451 nType = ControlType::Editbox;
453 // draw entire spinbox as a large edit box
454 if( nType == ControlType::Spinbox )
455 if( nPart == ControlPart::Entire )
456 nType = ControlType::Editbox;
458 int iPart(0), iState(0);
459 if( nType == ControlType::Scrollbar )
461 HRESULT hr;
462 if( nPart == ControlPart::ButtonUp )
464 iPart = SBP_ARROWBTN;
465 if( nState & ControlState::PRESSED )
466 iState = ABS_UPPRESSED;
467 else if( !(nState & ControlState::ENABLED) )
468 iState = ABS_UPDISABLED;
469 else if( nState & ControlState::ROLLOVER )
470 iState = ABS_UPHOT;
471 else
472 iState = ABS_UPNORMAL;
473 hr = DrawThemeBackground( hTheme, hDC, iPart, iState, &rc, nullptr);
474 return (hr == S_OK);
476 if( nPart == ControlPart::ButtonDown )
478 iPart = SBP_ARROWBTN;
479 if( nState & ControlState::PRESSED )
480 iState = ABS_DOWNPRESSED;
481 else if( !(nState & ControlState::ENABLED) )
482 iState = ABS_DOWNDISABLED;
483 else if( nState & ControlState::ROLLOVER )
484 iState = ABS_DOWNHOT;
485 else
486 iState = ABS_DOWNNORMAL;
487 hr = DrawThemeBackground( hTheme, hDC, iPart, iState, &rc, nullptr);
488 return (hr == S_OK);
490 if( nPart == ControlPart::ButtonLeft )
492 iPart = SBP_ARROWBTN;
493 if( nState & ControlState::PRESSED )
494 iState = ABS_LEFTPRESSED;
495 else if( !(nState & ControlState::ENABLED) )
496 iState = ABS_LEFTDISABLED;
497 else if( nState & ControlState::ROLLOVER )
498 iState = ABS_LEFTHOT;
499 else
500 iState = ABS_LEFTNORMAL;
501 hr = DrawThemeBackground( hTheme, hDC, iPart, iState, &rc, nullptr);
502 return (hr == S_OK);
504 if( nPart == ControlPart::ButtonRight )
506 iPart = SBP_ARROWBTN;
507 if( nState & ControlState::PRESSED )
508 iState = ABS_RIGHTPRESSED;
509 else if( !(nState & ControlState::ENABLED) )
510 iState = ABS_RIGHTDISABLED;
511 else if( nState & ControlState::ROLLOVER )
512 iState = ABS_RIGHTHOT;
513 else
514 iState = ABS_RIGHTNORMAL;
515 hr = DrawThemeBackground( hTheme, hDC, iPart, iState, &rc, nullptr);
516 return (hr == S_OK);
518 if( nPart == ControlPart::ThumbHorz || nPart == ControlPart::ThumbVert )
520 iPart = (nPart == ControlPart::ThumbHorz) ? SBP_THUMBBTNHORZ : SBP_THUMBBTNVERT;
521 if( nState & ControlState::PRESSED )
522 iState = SCRBS_PRESSED;
523 else if( !(nState & ControlState::ENABLED) )
524 iState = SCRBS_DISABLED;
525 else if( nState & ControlState::ROLLOVER )
526 iState = SCRBS_HOT;
527 else
528 iState = SCRBS_NORMAL;
530 SIZE sz;
531 GetThemePartSize(hTheme, hDC, iPart, iState, nullptr, TS_MIN, &sz);
532 GetThemePartSize(hTheme, hDC, iPart, iState, nullptr, TS_TRUE, &sz);
533 GetThemePartSize(hTheme, hDC, iPart, iState, nullptr, TS_DRAW, &sz);
535 hr = DrawThemeBackground( hTheme, hDC, iPart, iState, &rc, nullptr);
536 // paint gripper on thumb if enough space
537 if( ( (nPart == ControlPart::ThumbVert) && (rc.bottom-rc.top > 12) ) ||
538 ( (nPart == ControlPart::ThumbHorz) && (rc.right-rc.left > 12) ) )
540 iPart = (nPart == ControlPart::ThumbHorz) ? SBP_GRIPPERHORZ : SBP_GRIPPERVERT;
541 iState = 0;
542 DrawThemeBackground( hTheme, hDC, iPart, iState, &rc, nullptr);
544 return (hr == S_OK);
546 if( nPart == ControlPart::TrackHorzLeft || nPart == ControlPart::TrackHorzRight || nPart == ControlPart::TrackVertUpper || nPart == ControlPart::TrackVertLower )
548 switch( nPart )
550 case ControlPart::TrackHorzLeft: iPart = SBP_UPPERTRACKHORZ; break;
551 case ControlPart::TrackHorzRight: iPart = SBP_LOWERTRACKHORZ; break;
552 case ControlPart::TrackVertUpper: iPart = SBP_UPPERTRACKVERT; break;
553 case ControlPart::TrackVertLower: iPart = SBP_LOWERTRACKVERT; break;
554 default: break;
557 if( nState & ControlState::PRESSED )
558 iState = SCRBS_PRESSED;
559 else if( !(nState & ControlState::ENABLED) )
560 iState = SCRBS_DISABLED;
561 else if( nState & ControlState::ROLLOVER )
562 iState = SCRBS_HOT;
563 else
564 iState = SCRBS_NORMAL;
565 hr = DrawThemeBackground( hTheme, hDC, iPart, iState, &rc, nullptr);
566 return (hr == S_OK);
569 if( nType == ControlType::SpinButtons && nPart == ControlPart::AllButtons )
571 if( aValue.getType() == ControlType::SpinButtons )
573 const SpinbuttonValue* pValue = (aValue.getType() == ControlType::SpinButtons) ? static_cast<const SpinbuttonValue*>(&aValue) : nullptr;
575 RECT rect;
576 ImplConvertSpinbuttonValues( pValue->mnUpperPart, pValue->mnUpperState, pValue->maUpperRect, &iPart, &iState, &rect );
577 bool bOk = ImplDrawTheme( hTheme, hDC, iPart, iState, rect, aCaption);
579 if( bOk )
581 ImplConvertSpinbuttonValues( pValue->mnLowerPart, pValue->mnLowerState, pValue->maLowerRect, &iPart, &iState, &rect );
582 bOk = ImplDrawTheme( hTheme, hDC, iPart, iState, rect, aCaption);
585 return bOk;
588 if( nType == ControlType::Spinbox )
590 if( nPart == ControlPart::AllButtons )
592 if( aValue.getType() == ControlType::SpinButtons )
594 const SpinbuttonValue *pValue = static_cast<const SpinbuttonValue*>(&aValue);
596 RECT rect;
597 ImplConvertSpinbuttonValues( pValue->mnUpperPart, pValue->mnUpperState, pValue->maUpperRect, &iPart, &iState, &rect );
598 bool bOk = ImplDrawTheme( hTheme, hDC, iPart, iState, rect, aCaption);
600 if( bOk )
602 ImplConvertSpinbuttonValues( pValue->mnLowerPart, pValue->mnLowerState, pValue->maLowerRect, &iPart, &iState, &rect );
603 bOk = ImplDrawTheme( hTheme, hDC, iPart, iState, rect, aCaption);
606 return bOk;
610 if( nPart == ControlPart::ButtonDown )
612 iPart = SPNP_DOWN;
613 if( nState & ControlState::PRESSED )
614 iState = DNS_PRESSED;
615 else if( !(nState & ControlState::ENABLED) )
616 iState = DNS_DISABLED;
617 else if( nState & ControlState::ROLLOVER )
618 iState = DNS_HOT;
619 else
620 iState = DNS_NORMAL;
622 if( nPart == ControlPart::ButtonUp )
624 iPart = SPNP_UP;
625 if( nState & ControlState::PRESSED )
626 iState = UPS_PRESSED;
627 else if( !(nState & ControlState::ENABLED) )
628 iState = UPS_DISABLED;
629 else if( nState & ControlState::ROLLOVER )
630 iState = UPS_HOT;
631 else
632 iState = UPS_NORMAL;
634 if( nPart == ControlPart::ButtonRight )
636 iPart = SPNP_DOWNHORZ;
637 if( nState & ControlState::PRESSED )
638 iState = DNHZS_PRESSED;
639 else if( !(nState & ControlState::ENABLED) )
640 iState = DNHZS_DISABLED;
641 else if( nState & ControlState::ROLLOVER )
642 iState = DNHZS_HOT;
643 else
644 iState = DNHZS_NORMAL;
646 if( nPart == ControlPart::ButtonLeft )
648 iPart = SPNP_UPHORZ;
649 if( nState & ControlState::PRESSED )
650 iState = UPHZS_PRESSED;
651 else if( !(nState & ControlState::ENABLED) )
652 iState = UPHZS_DISABLED;
653 else if( nState & ControlState::ROLLOVER )
654 iState = UPHZS_HOT;
655 else
656 iState = UPHZS_NORMAL;
658 if( nPart == ControlPart::ButtonLeft || nPart == ControlPart::ButtonRight || nPart == ControlPart::ButtonUp || nPart == ControlPart::ButtonDown )
659 return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
661 if( nType == ControlType::Combobox )
663 if( nPart == ControlPart::ButtonDown )
665 iPart = CP_DROPDOWNBUTTON;
666 if( nState & ControlState::PRESSED )
667 iState = CBXS_PRESSED;
668 else if( !(nState & ControlState::ENABLED) )
669 iState = CBXS_DISABLED;
670 else if( nState & ControlState::ROLLOVER )
671 iState = CBXS_HOT;
672 else
673 iState = CBXS_NORMAL;
674 return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
677 if( nType == ControlType::Pushbutton )
679 iPart = BP_PUSHBUTTON;
680 if( nState & ControlState::PRESSED )
681 iState = PBS_PRESSED;
682 else if( !(nState & ControlState::ENABLED) )
683 iState = PBS_DISABLED;
684 else if( nState & ControlState::ROLLOVER )
685 iState = PBS_HOT;
686 else if( nState & ControlState::DEFAULT )
687 iState = PBS_DEFAULTED;
688 //else if( nState & ControlState::FOCUSED )
689 // iState = PBS_DEFAULTED; // may need to draw focus rect
690 else
691 iState = PBS_NORMAL;
693 return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
696 if( nType == ControlType::Radiobutton )
698 iPart = BP_RADIOBUTTON;
699 bool bChecked = ( aValue.getTristateVal() == ButtonValue::On );
701 if( nState & ControlState::PRESSED )
702 iState = bChecked ? RBS_CHECKEDPRESSED : RBS_UNCHECKEDPRESSED;
703 else if( !(nState & ControlState::ENABLED) )
704 iState = bChecked ? RBS_CHECKEDDISABLED : RBS_UNCHECKEDDISABLED;
705 else if( nState & ControlState::ROLLOVER )
706 iState = bChecked ? RBS_CHECKEDHOT : RBS_UNCHECKEDHOT;
707 else
708 iState = bChecked ? RBS_CHECKEDNORMAL : RBS_UNCHECKEDNORMAL;
710 //if( nState & ControlState::FOCUSED )
711 // iState |= PBS_DEFAULTED; // may need to draw focus rect
713 return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
716 if( nType == ControlType::Checkbox )
718 iPart = BP_CHECKBOX;
719 ButtonValue v = aValue.getTristateVal();
721 if( nState & ControlState::PRESSED )
722 iState = (v == ButtonValue::On) ? CBS_CHECKEDPRESSED :
723 ( (v == ButtonValue::Off) ? CBS_UNCHECKEDPRESSED : CBS_MIXEDPRESSED );
724 else if( !(nState & ControlState::ENABLED) )
725 iState = (v == ButtonValue::On) ? CBS_CHECKEDDISABLED :
726 ( (v == ButtonValue::Off) ? CBS_UNCHECKEDDISABLED : CBS_MIXEDDISABLED );
727 else if( nState & ControlState::ROLLOVER )
728 iState = (v == ButtonValue::On) ? CBS_CHECKEDHOT :
729 ( (v == ButtonValue::Off) ? CBS_UNCHECKEDHOT : CBS_MIXEDHOT );
730 else
731 iState = (v == ButtonValue::On) ? CBS_CHECKEDNORMAL :
732 ( (v == ButtonValue::Off) ? CBS_UNCHECKEDNORMAL : CBS_MIXEDNORMAL );
734 //if( nState & ControlState::FOCUSED )
735 // iState |= PBS_DEFAULTED; // may need to draw focus rect
737 //SIZE sz;
738 //THEMESIZE eSize = TS_DRAW; // TS_MIN, TS_TRUE, TS_DRAW
739 //GetThemePartSize( hTheme, hDC, iPart, iState, &rc, eSize, &sz);
741 return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
744 if (nType == ControlType::Editbox)
746 iPart = EP_EDITBORDER_NOSCROLL;
747 if( !(nState & ControlState::ENABLED) )
748 iState = EPSN_DISABLED;
749 else if( nState & ControlState::FOCUSED )
750 iState = EPSN_FOCUSED;
751 else if( nState & ControlState::ROLLOVER )
752 iState = EPSN_HOT;
753 else
754 iState = EPSN_NORMAL;
756 return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
759 if (nType == ControlType::MultilineEditbox)
761 iPart = EP_EDITTEXT;
762 if( !(nState & ControlState::ENABLED) )
763 iState = ETS_DISABLED;
764 else if( nState & ControlState::FOCUSED )
765 iState = ETS_FOCUSED;
766 else if( nState & ControlState::ROLLOVER )
767 iState = ETS_HOT;
768 else
769 iState = ETS_NORMAL;
771 return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
774 if( nType == ControlType::Listbox )
776 if( nPart == ControlPart::Entire || nPart == ControlPart::ListboxWindow )
778 iPart = LVP_EMPTYTEXT; // ??? no idea which part to choose here
779 return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
783 if( nType == ControlType::TabPane )
785 // tabpane in tabcontrols gets drawn in "darkmode" as if it was a
786 // a "light" theme, so bodge this by drawing a frame directly
787 if (bUseDarkMode)
789 Color aColor(Application::GetSettings().GetStyleSettings().GetDisableColor());
790 ScopedHBRUSH hbrush(CreateSolidBrush(RGB(aColor.GetRed(),
791 aColor.GetGreen(),
792 aColor.GetBlue())));
793 FrameRect(hDC, &rc, hbrush.get());
794 return true;
796 iPart = TABP_PANE;
797 return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
800 if( nType == ControlType::TabBody )
802 // tabbody in main window gets drawn in white in "darkmode", so bodge this here
803 if (bUseDarkMode)
805 Color aColor(Application::GetSettings().GetStyleSettings().GetWindowColor());
806 ScopedHBRUSH hbrush(CreateSolidBrush(RGB(aColor.GetRed(),
807 aColor.GetGreen(),
808 aColor.GetBlue())));
809 FillRect(hDC, &rc, hbrush.get());
810 return true;
813 iPart = TABP_BODY;
814 return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
817 if( nType == ControlType::TabItem )
819 iPart = TABP_TABITEMLEFTEDGE;
820 rc.bottom--;
822 OSL_ASSERT( aValue.getType() == ControlType::TabItem );
824 const TabitemValue& rValue = static_cast<const TabitemValue&>(aValue);
825 if (rValue.isBothAligned())
827 iPart = TABP_TABITEMLEFTEDGE;
828 rc.right--;
830 else if (rValue.isLeftAligned())
831 iPart = TABP_TABITEMLEFTEDGE;
832 else if (rValue.isRightAligned())
833 iPart = TABP_TABITEMRIGHTEDGE;
834 else
835 iPart = TABP_TABITEM;
837 if( !(nState & ControlState::ENABLED) )
838 iState = TILES_DISABLED;
839 else if( nState & ControlState::SELECTED )
841 iState = TILES_SELECTED;
842 // increase the selected tab
843 rc.left-=2;
844 if (rValue.isBothAligned())
846 if (rValue.isLeftAligned() || rValue.isNotAligned())
847 rc.right+=2;
848 if (rValue.isRightAligned())
849 rc.right+=1;
851 rc.top-=2;
852 rc.bottom+=2;
854 else if( nState & ControlState::ROLLOVER )
855 iState = TILES_HOT;
856 else if( nState & ControlState::FOCUSED )
857 iState = TILES_FOCUSED; // may need to draw focus rect
858 else
859 iState = TILES_NORMAL;
861 // tabitem in tabcontrols gets drawn in "darkmode" as if it was a
862 // a "light" theme, so bodge this by drawing with a button instead
863 if (bUseDarkMode)
865 Color aColor;
866 if (iState == TILES_SELECTED)
867 aColor = Application::GetSettings().GetStyleSettings().GetActiveTabColor();
868 else
869 aColor = Application::GetSettings().GetStyleSettings().GetInactiveTabColor();
870 ScopedHBRUSH hbrush(CreateSolidBrush(RGB(aColor.GetRed(),
871 aColor.GetGreen(),
872 aColor.GetBlue())));
873 FillRect(hDC, &rc, hbrush.get());
875 aColor = Application::GetSettings().GetStyleSettings().GetDisableColor();
876 ScopedSelectedHPEN hPen(hDC, CreatePen(PS_SOLID, 1, RGB(aColor.GetRed(),
877 aColor.GetGreen(),
878 aColor.GetBlue())));
879 POINT apt[4];
880 apt[0].x = rc.left;
881 apt[0].y = rc.bottom - (iPart == TABP_TABITEMLEFTEDGE ? 1 : 2);
882 apt[1].x = rc.left;
883 apt[1].y = rc.top;
884 apt[2].x = rc.right;
885 apt[2].y = rc.top;
886 apt[3].x = rc.right;
887 apt[3].y = rc.bottom - 1;
888 Polyline(hDC, apt, SAL_N_ELEMENTS(apt));
889 return true;
892 return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
895 if( nType == ControlType::Toolbar )
897 if( nPart == ControlPart::Button )
899 iPart = TP_BUTTON;
900 bool bChecked = ( aValue.getTristateVal() == ButtonValue::On );
901 if( !(nState & ControlState::ENABLED) )
902 //iState = TS_DISABLED;
903 // disabled buttons are typically not painted at all but we need visual
904 // feedback when travelling by keyboard over disabled entries
905 iState = TS_HOT;
906 else if( nState & ControlState::PRESSED )
907 iState = TS_PRESSED;
908 else if( nState & ControlState::ROLLOVER )
909 iState = bChecked ? TS_HOTCHECKED : TS_HOT;
910 else
911 iState = bChecked ? TS_CHECKED : TS_NORMAL;
912 return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
914 else if( nPart == ControlPart::ThumbHorz || nPart == ControlPart::ThumbVert )
916 // the vertical gripper is not supported in most themes and it makes no
917 // sense to only support horizontal gripper
918 //iPart = (nPart == ControlPart::ThumbHorz) ? RP_GRIPPERVERT : RP_GRIPPER;
919 //return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
921 else if( nPart == ControlPart::DrawBackgroundHorz || nPart == ControlPart::DrawBackgroundVert )
923 if( aValue.getType() == ControlType::Toolbar )
925 const ToolbarValue *pValue = static_cast<const ToolbarValue*>(&aValue);
926 if( pValue->mbIsTopDockingArea )
927 rc.top = 0; // extend potential gradient to cover menu bar as well
930 // toolbar in main window gets drawn in white in "darkmode", so bodge this here
931 if (bUseDarkMode)
933 Color aColor(Application::GetSettings().GetStyleSettings().GetWindowColor());
934 ScopedHBRUSH hbrush(CreateSolidBrush(RGB(aColor.GetRed(),
935 aColor.GetGreen(),
936 aColor.GetBlue())));
937 FillRect(hDC, &rc, hbrush.get());
938 return true;
941 // make it more compatible with Aero
942 if (ImplGetSVData()->maNWFData.mbDockingAreaAvoidTBFrames &&
943 !Application::GetSettings().GetStyleSettings().GetHighContrastMode())
945 impl_drawAeroToolbar( hDC, rc, nPart == ControlPart::DrawBackgroundHorz );
946 return true;
949 return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
953 if( nType == ControlType::Menubar )
955 if( nPart == ControlPart::Entire )
957 if( aValue.getType() == ControlType::Menubar )
959 const MenubarValue *pValue = static_cast<const MenubarValue*>(&aValue);
960 rc.bottom += pValue->maTopDockingAreaHeight; // extend potential gradient to cover docking area as well
962 // menubar in main window gets drawn in white in "darkmode", so bodge this here
963 if (bUseDarkMode)
965 Color aColor(Application::GetSettings().GetStyleSettings().GetWindowColor());
966 ScopedHBRUSH hbrush(CreateSolidBrush(RGB(aColor.GetRed(),
967 aColor.GetGreen(),
968 aColor.GetBlue())));
969 FillRect(hDC, &rc, hbrush.get());
970 return true;
973 // make it more compatible with Aero
974 if (ImplGetSVData()->maNWFData.mbDockingAreaAvoidTBFrames &&
975 !Application::GetSettings().GetStyleSettings().GetHighContrastMode())
977 impl_drawAeroToolbar( hDC, rc, true );
978 return true;
981 return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
983 else if( nPart == ControlPart::MenuItem )
985 if( nState & ControlState::ENABLED )
987 if( nState & ControlState::SELECTED )
988 iState = MBI_PUSHED;
989 else if( nState & ControlState::ROLLOVER )
990 iState = MBI_HOT;
991 else
992 iState = MBI_NORMAL;
994 if(GetSalData()->mbThemeMenuSupport && Application::GetSettings().GetStyleSettings().GetHighContrastMode()
995 && ( nState & (ControlState::SELECTED | nState & ControlState::ROLLOVER )))
997 Color aColor(Application::GetSettings().GetStyleSettings().GetHighlightColor());
998 ScopedHBRUSH hbrush(CreateSolidBrush(RGB(aColor.GetRed(),
999 aColor.GetGreen(),
1000 aColor.GetBlue())));
1001 FillRect(hDC, &rc, hbrush.get());
1002 return true;
1005 else
1007 if( nState & ControlState::SELECTED )
1008 iState = MBI_DISABLEDPUSHED;
1009 else if( nState & ControlState::ROLLOVER )
1010 iState = MBI_DISABLEDHOT;
1011 else
1012 iState = MBI_DISABLED;
1014 return ImplDrawTheme( hTheme, hDC, MENU_BARITEM, iState, rc, aCaption );
1018 if( nType == ControlType::Progress || nType == ControlType::LevelBar )
1020 if( nPart != ControlPart::Entire )
1021 return false;
1023 int nPartIdBackground = PP_BAR;
1024 if( nType == ControlType::LevelBar )
1026 nPartIdBackground = PP_TRANSPARENTBAR;
1027 iState = PBBS_PARTIAL;
1030 if( ! ImplDrawTheme( hTheme, hDC, nPartIdBackground, iState, rc, aCaption) )
1031 return false;
1032 RECT aProgressRect = rc;
1033 if( GetThemeBackgroundContentRect( hTheme, hDC, PP_BAR, iState, &rc, &aProgressRect) != S_OK )
1034 return false;
1036 tools::Long nProgressWidth = aValue.getNumericVal();
1037 nProgressWidth *= (aProgressRect.right - aProgressRect.left);
1038 nProgressWidth /= (rc.right - rc.left);
1039 if( AllSettings::GetLayoutRTL() )
1040 aProgressRect.left = aProgressRect.right - nProgressWidth;
1041 else
1042 aProgressRect.right = aProgressRect.left + nProgressWidth;
1044 if (nType == ControlType::LevelBar)
1046 const auto nPercentage
1047 = aValue.getNumericVal() * 100 / std::max(LONG{ 1 }, (rc.right - rc.left));
1049 COLORREF aBrushColor{};
1050 if (nPercentage < 25)
1051 aBrushColor = RGB(255, 0, 0);
1052 else if (nPercentage < 50)
1053 aBrushColor = RGB(255, 255, 0);
1054 else if (nPercentage < 75)
1055 aBrushColor = RGB(0, 0, 255);
1056 else
1057 aBrushColor = RGB(0, 255, 0);
1059 ScopedHBRUSH hBrush(CreateSolidBrush(aBrushColor));
1060 FillRect(hDC, &aProgressRect, hBrush.get());
1061 return true;
1064 return ImplDrawTheme( hTheme, hDC, PP_CHUNK, iState, aProgressRect, aCaption );
1067 if( nType == ControlType::Slider )
1069 iPart = (nPart == ControlPart::TrackHorzArea) ? TKP_TRACK : TKP_TRACKVERT;
1070 iState = (nPart == ControlPart::TrackHorzArea) ? static_cast<int>(TRS_NORMAL) : static_cast<int>(TRVS_NORMAL);
1072 tools::Rectangle aTrackRect = ImplGetThemeRect( hTheme, hDC, iPart, iState, tools::Rectangle() );
1073 RECT aTRect = rc;
1074 if( nPart == ControlPart::TrackHorzArea )
1076 tools::Long nH = aTrackRect.GetHeight();
1077 aTRect.top += (rc.bottom - rc.top - nH)/2;
1078 aTRect.bottom = aTRect.top + nH;
1080 else
1082 tools::Long nW = aTrackRect.GetWidth();
1083 aTRect.left += (rc.right - rc.left - nW)/2;
1084 aTRect.right = aTRect.left + nW;
1086 ImplDrawTheme( hTheme, hDC, iPart, iState, aTRect, aCaption );
1088 RECT aThumbRect;
1089 OSL_ASSERT( aValue.getType() == ControlType::Slider );
1090 const SliderValue* pVal = static_cast<const SliderValue*>(&aValue);
1091 aThumbRect.left = pVal->maThumbRect.Left();
1092 aThumbRect.top = pVal->maThumbRect.Top();
1093 aThumbRect.right = pVal->maThumbRect.Right();
1094 aThumbRect.bottom = pVal->maThumbRect.Bottom();
1095 iPart = (nPart == ControlPart::TrackHorzArea) ? TKP_THUMB : TKP_THUMBVERT;
1096 iState = (nState & ControlState::ENABLED) ? TUS_NORMAL : TUS_DISABLED;
1097 return ImplDrawTheme( hTheme, hDC, iPart, iState, aThumbRect, aCaption );
1100 if( nType == ControlType::ListNode )
1102 if( nPart != ControlPart::Entire )
1103 return false;
1105 ButtonValue aButtonValue = aValue.getTristateVal();
1106 iPart = TVP_GLYPH;
1107 switch( aButtonValue )
1109 case ButtonValue::On:
1110 iState = GLPS_OPENED;
1111 break;
1112 case ButtonValue::Off:
1113 iState = GLPS_CLOSED;
1114 break;
1115 default:
1116 return false;
1118 return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption );
1121 if( GetSalData()->mbThemeMenuSupport )
1123 if( nType == ControlType::MenuPopup )
1125 if( nPart == ControlPart::Entire )
1127 RECT aGutterRC = rc;
1128 if( AllSettings::GetLayoutRTL() )
1130 aGutterRC.right -= aValue.getNumericVal()+1;
1131 aGutterRC.left = aGutterRC.right-3;
1133 else
1135 aGutterRC.left += aValue.getNumericVal();
1136 aGutterRC.right = aGutterRC.left+3;
1138 return
1139 ImplDrawTheme( hTheme, hDC, MENU_POPUPBACKGROUND, 0, rc, aCaption ) &&
1140 ImplDrawTheme( hTheme, hDC, MENU_POPUPGUTTER, 0, aGutterRC, aCaption )
1143 else if( nPart == ControlPart::MenuItem )
1145 if( nState & ControlState::ENABLED )
1146 iState = (nState & ControlState::SELECTED) ? MPI_HOT : MPI_NORMAL;
1147 else
1148 iState = (nState & ControlState::SELECTED) ? MPI_DISABLEDHOT : MPI_DISABLED;
1149 return ImplDrawTheme( hTheme, hDC, MENU_POPUPITEM, iState, rc, aCaption );
1151 else if( nPart == ControlPart::MenuItemCheckMark || nPart == ControlPart::MenuItemRadioMark )
1153 if (nState & ControlState::PRESSED)
1154 return implDrawNativeMenuMark(hDC, hTheme, rc, nPart, nState, aCaption);
1155 else
1156 return true; // unchecked: do nothing
1158 else if( nPart == ControlPart::Separator )
1160 // adjust for gutter position
1161 if( AllSettings::GetLayoutRTL() )
1162 rc.right -= aValue.getNumericVal()+1;
1163 else
1164 rc.left += aValue.getNumericVal()+1;
1165 tools::Rectangle aRect( ImplGetThemeRect( hTheme, hDC,
1166 MENU_POPUPSEPARATOR, 0, tools::Rectangle( rc.left, rc.top, rc.right, rc.bottom ) ) );
1167 // center the separator inside the passed rectangle
1168 auto const nDY = ((rc.bottom - rc.top + 1) - aRect.GetHeight()) / 2;
1169 rc.top += nDY;
1170 rc.bottom = rc.top+aRect.GetHeight()-1;
1171 return ImplDrawTheme( hTheme, hDC, MENU_POPUPSEPARATOR, 0, rc, aCaption );
1176 return false;
1179 bool WinSalGraphics::drawNativeControl( ControlType nType,
1180 ControlPart nPart,
1181 const tools::Rectangle& rControlRegion,
1182 ControlState nState,
1183 const ImplControlValue& aValue,
1184 const OUString& aCaption,
1185 const Color& /*rBackgroundColor*/ )
1187 bool bOk = false;
1188 HTHEME hTheme = nullptr;
1190 tools::Rectangle buttonRect = rControlRegion;
1191 tools::Rectangle cacheRect = rControlRegion;
1192 Size keySize = cacheRect.GetSize();
1194 WinSalGraphicsImplBase* pImpl = mWinSalGraphicsImplBase;
1195 if( !pImpl->UseRenderNativeControl())
1196 pImpl = nullptr;
1198 // tdf#95618 - A few controls render outside the region they're given.
1199 if (pImpl && nType == ControlType::TabItem)
1201 tools::Rectangle rNativeBoundingRegion;
1202 tools::Rectangle rNativeContentRegion;
1203 if (getNativeControlRegion(nType, nPart, rControlRegion, nState, aValue, aCaption,
1204 rNativeBoundingRegion, rNativeContentRegion))
1206 cacheRect = rNativeBoundingRegion;
1207 keySize = rNativeBoundingRegion.GetSize();
1212 ControlCacheKey aControlCacheKey(nType, nPart, nState, keySize);
1213 if (pImpl != nullptr && pImpl->TryRenderCachedNativeControl(aControlCacheKey, buttonRect.Left(), buttonRect.Top()))
1215 return true;
1218 const bool bUseDarkMode = UseDarkMode();
1219 if (bUseDarkMode)
1220 SetWindowTheme(mhWnd, L"Explorer", nullptr);
1222 switch( nType )
1224 case ControlType::Pushbutton:
1225 case ControlType::Radiobutton:
1226 case ControlType::Checkbox:
1227 hTheme = getThemeHandle(mhWnd, L"Button", mWinSalGraphicsImplBase);
1228 break;
1229 case ControlType::Scrollbar:
1230 if (bUseDarkMode)
1232 // tdf#153273 undo the earlier SetWindowTheme, and use an explicit Explorer::Scrollbar
1233 // a) with "Scrollbar" and SetWindowTheme(... "Explorer" ...) then scrollbars in dialog
1234 // and main windows are dark, but dropdowns are light
1235 // b) with "Explorer::Scrollbar" and SetWindowTheme(... "Explorer" ...) then scrollbars
1236 // in dropdowns are dark, but scrollbars in dialogs and main windows are sort of "extra
1237 // dark"
1238 // c) with "Explorer::Scrollbar" and no SetWindowTheme both cases are dark
1239 SetWindowTheme(mhWnd, nullptr, nullptr);
1240 hTheme = getThemeHandle(mhWnd, L"Explorer::Scrollbar", mWinSalGraphicsImplBase);
1242 else
1243 hTheme = getThemeHandle(mhWnd, L"Scrollbar", mWinSalGraphicsImplBase);
1244 break;
1245 case ControlType::Combobox:
1246 if( nPart == ControlPart::Entire )
1248 if (bUseDarkMode && !(nState & ControlState::FOCUSED))
1249 SetWindowTheme(mhWnd, L"CFD", nullptr);
1250 hTheme = getThemeHandle(mhWnd, L"Edit", mWinSalGraphicsImplBase);
1252 else if( nPart == ControlPart::ButtonDown )
1254 if (bUseDarkMode)
1255 SetWindowTheme(mhWnd, L"CFD", nullptr);
1256 hTheme = getThemeHandle(mhWnd, L"Combobox", mWinSalGraphicsImplBase);
1258 break;
1259 case ControlType::Spinbox:
1260 if( nPart == ControlPart::Entire )
1262 if (bUseDarkMode && !(nState & ControlState::FOCUSED))
1263 SetWindowTheme(mhWnd, L"CFD", nullptr);
1264 hTheme = getThemeHandle(mhWnd, L"Edit", mWinSalGraphicsImplBase);
1266 else
1267 hTheme = getThemeHandle(mhWnd, L"Spin", mWinSalGraphicsImplBase);
1268 break;
1269 case ControlType::SpinButtons:
1270 hTheme = getThemeHandle(mhWnd, L"Spin", mWinSalGraphicsImplBase);
1271 break;
1272 case ControlType::Editbox:
1273 if (bUseDarkMode && !(nState & ControlState::FOCUSED))
1274 SetWindowTheme(mhWnd, L"CFD", nullptr);
1275 hTheme = getThemeHandle(mhWnd, L"Edit", mWinSalGraphicsImplBase);
1276 break;
1277 case ControlType::MultilineEditbox:
1278 hTheme = getThemeHandle(mhWnd, L"Edit", mWinSalGraphicsImplBase);
1279 break;
1280 case ControlType::Listbox:
1281 if( nPart == ControlPart::Entire || nPart == ControlPart::ListboxWindow )
1282 hTheme = getThemeHandle(mhWnd, L"Listview", mWinSalGraphicsImplBase);
1283 else if( nPart == ControlPart::ButtonDown )
1285 if (bUseDarkMode)
1286 SetWindowTheme(mhWnd, L"CFD", nullptr);
1287 hTheme = getThemeHandle(mhWnd, L"Combobox", mWinSalGraphicsImplBase);
1289 break;
1290 case ControlType::TabBody:
1291 hTheme = getThemeHandle(mhWnd, L"Tab", mWinSalGraphicsImplBase);
1292 break;
1293 case ControlType::TabPane:
1294 case ControlType::TabItem:
1295 hTheme = getThemeHandle(mhWnd, L"Tab", mWinSalGraphicsImplBase);
1296 break;
1297 case ControlType::Toolbar:
1298 if( nPart == ControlPart::Entire || nPart == ControlPart::Button )
1299 hTheme = getThemeHandle(mhWnd, L"Toolbar", mWinSalGraphicsImplBase);
1300 else
1301 // use rebar for grip and background
1302 hTheme = getThemeHandle(mhWnd, L"Rebar", mWinSalGraphicsImplBase);
1303 break;
1304 case ControlType::Menubar:
1305 if( nPart == ControlPart::Entire )
1306 hTheme = getThemeHandle(mhWnd, L"Rebar", mWinSalGraphicsImplBase);
1307 else if( GetSalData()->mbThemeMenuSupport )
1309 if( nPart == ControlPart::MenuItem )
1310 hTheme = getThemeHandle(mhWnd, L"Menu", mWinSalGraphicsImplBase);
1312 break;
1313 case ControlType::Progress:
1314 case ControlType::LevelBar:
1315 if( nPart == ControlPart::Entire )
1316 hTheme = getThemeHandle(mhWnd, L"Progress", mWinSalGraphicsImplBase);
1317 break;
1318 case ControlType::ListNode:
1319 if( nPart == ControlPart::Entire )
1320 hTheme = getThemeHandle(mhWnd, L"TreeView", mWinSalGraphicsImplBase);
1321 break;
1322 case ControlType::Slider:
1323 if( nPart == ControlPart::TrackHorzArea || nPart == ControlPart::TrackVertArea )
1324 hTheme = getThemeHandle(mhWnd, L"Trackbar", mWinSalGraphicsImplBase);
1325 break;
1326 case ControlType::MenuPopup:
1327 if( GetSalData()->mbThemeMenuSupport )
1329 if( nPart == ControlPart::Entire || nPart == ControlPart::MenuItem ||
1330 nPart == ControlPart::MenuItemCheckMark || nPart == ControlPart::MenuItemRadioMark ||
1331 nPart == ControlPart::Separator
1333 hTheme = getThemeHandle(mhWnd, L"Menu", mWinSalGraphicsImplBase);
1335 break;
1336 default:
1337 hTheme = nullptr;
1338 break;
1341 if( !hTheme )
1343 if (bUseDarkMode)
1344 SetWindowTheme(mhWnd, nullptr, nullptr);
1345 return false;
1348 RECT rc;
1349 rc.left = buttonRect.Left();
1350 rc.right = buttonRect.Right()+1;
1351 rc.top = buttonRect.Top();
1352 rc.bottom = buttonRect.Bottom()+1;
1354 OUString aCaptionStr(aCaption.replace('~', '&')); // translate mnemonics
1356 if (pImpl == nullptr)
1358 // set default text alignment
1359 int ta = SetTextAlign(getHDC(), TA_LEFT|TA_TOP|TA_NOUPDATECP);
1361 bOk = ImplDrawNativeControl(getHDC(), hTheme, rc, nType, nPart, nState, aValue, aCaptionStr, bUseDarkMode);
1363 // restore alignment
1364 SetTextAlign(getHDC(), ta);
1366 else
1368 // We can do OpenGL/Skia
1369 std::unique_ptr<CompatibleDC> aBlackDC(CompatibleDC::create(*this, cacheRect.Left(), cacheRect.Top(), cacheRect.GetWidth()+1, cacheRect.GetHeight()+1));
1370 SetTextAlign(aBlackDC->getCompatibleHDC(), TA_LEFT|TA_TOP|TA_NOUPDATECP);
1371 aBlackDC->fill(RGB(0, 0, 0));
1373 std::unique_ptr<CompatibleDC> aWhiteDC(CompatibleDC::create(*this, cacheRect.Left(), cacheRect.Top(), cacheRect.GetWidth()+1, cacheRect.GetHeight()+1));
1374 SetTextAlign(aWhiteDC->getCompatibleHDC(), TA_LEFT|TA_TOP|TA_NOUPDATECP);
1375 aWhiteDC->fill(RGB(0xff, 0xff, 0xff));
1377 if (ImplDrawNativeControl(aBlackDC->getCompatibleHDC(), hTheme, rc, nType, nPart, nState, aValue, aCaptionStr, bUseDarkMode) &&
1378 ImplDrawNativeControl(aWhiteDC->getCompatibleHDC(), hTheme, rc, nType, nPart, nState, aValue, aCaptionStr, bUseDarkMode))
1380 bOk = pImpl->RenderAndCacheNativeControl(*aWhiteDC, *aBlackDC, cacheRect.Left(), cacheRect.Top(), aControlCacheKey);
1384 if (bUseDarkMode)
1385 SetWindowTheme(mhWnd, nullptr, nullptr);
1386 return bOk;
1389 bool WinSalGraphics::getNativeControlRegion( ControlType nType,
1390 ControlPart nPart,
1391 const tools::Rectangle& rControlRegion,
1392 ControlState nState,
1393 const ImplControlValue& rControlValue,
1394 const OUString&,
1395 tools::Rectangle &rNativeBoundingRegion,
1396 tools::Rectangle &rNativeContentRegion )
1398 bool bRet = false;
1400 // FIXME: rNativeBoundingRegion has a different origin
1401 // depending on which part is used; horrors.
1403 HDC hDC = GetDC( mhWnd );
1404 if( nType == ControlType::Toolbar )
1406 if( nPart == ControlPart::ThumbHorz || nPart == ControlPart::ThumbVert )
1409 // the vertical gripper is not supported in most themes and it makes no
1410 // sense to only support horizontal gripper
1412 HTHEME hTheme = getThemeHandle(mhWnd, L"Rebar", mWinSalGraphicsImplBase);
1413 if( hTheme )
1415 tools::Rectangle aRect( ImplGetThemeRect( hTheme, hDC, nPart == ControlPart::ThumbHorz ? RP_GRIPPERVERT : RP_GRIPPER,
1416 0, rControlRegion.GetBoundRect() ) );
1417 if( nPart == ControlPart::ThumbHorz && !aRect.IsEmpty() )
1419 tools::Rectangle aVertRect( 0, 0, aRect.getHeight(), aRect.getWidth() );
1420 rNativeContentRegion = aVertRect;
1422 else
1423 rNativeContentRegion = aRect;
1424 rNativeBoundingRegion = rNativeContentRegion;
1425 if( !rNativeContentRegion.IsEmpty() )
1426 bRet = TRUE;
1430 if( nPart == ControlPart::Button )
1432 HTHEME hTheme = getThemeHandle(mhWnd, L"Toolbar", mWinSalGraphicsImplBase);
1433 if( hTheme )
1435 tools::Rectangle aRect( ImplGetThemeRect( hTheme, hDC, TP_SPLITBUTTONDROPDOWN,
1436 TS_HOT, rControlRegion ) );
1437 rNativeContentRegion = aRect;
1438 rNativeBoundingRegion = rNativeContentRegion;
1439 if( !rNativeContentRegion.IsEmpty() )
1440 bRet = true;
1444 if( nType == ControlType::Progress && nPart == ControlPart::Entire )
1446 HTHEME hTheme = getThemeHandle(mhWnd, L"Progress", mWinSalGraphicsImplBase);
1447 if( hTheme )
1449 tools::Rectangle aRect( ImplGetThemeRect( hTheme, hDC, PP_BAR,
1450 0, rControlRegion ) );
1451 rNativeContentRegion = aRect;
1452 rNativeBoundingRegion = rNativeContentRegion;
1453 if( !rNativeContentRegion.IsEmpty() )
1454 bRet = true;
1457 if( (nType == ControlType::Listbox || nType == ControlType::Combobox ) && nPart == ControlPart::Entire )
1459 HTHEME hTheme = getThemeHandle(mhWnd, L"Combobox", mWinSalGraphicsImplBase);
1460 if( hTheme )
1462 tools::Rectangle aBoxRect( rControlRegion );
1463 tools::Rectangle aRect( ImplGetThemeRect( hTheme, hDC, CP_DROPDOWNBUTTON,
1464 CBXS_NORMAL, aBoxRect ) );
1465 if( aRect.GetHeight() > aBoxRect.GetHeight() )
1466 aBoxRect.SetBottom( aBoxRect.Top() + aRect.GetHeight() );
1467 if( aRect.GetWidth() > aBoxRect.GetWidth() )
1468 aBoxRect.SetRight( aBoxRect.Left() + aRect.GetWidth() );
1469 rNativeContentRegion = aBoxRect;
1470 rNativeBoundingRegion = rNativeContentRegion;
1471 if( !aRect.IsEmpty() )
1472 bRet = true;
1476 if( (nType == ControlType::Editbox || nType == ControlType::Spinbox) && nPart == ControlPart::Entire )
1478 HTHEME hTheme = getThemeHandle(mhWnd, L"Edit", mWinSalGraphicsImplBase);
1479 if( hTheme )
1481 // get border size
1482 tools::Rectangle aBoxRect( rControlRegion );
1483 tools::Rectangle aRect( ImplGetThemeRect( hTheme, hDC, EP_BACKGROUNDWITHBORDER,
1484 EBWBS_HOT, aBoxRect ) );
1485 // ad app font height
1486 NONCLIENTMETRICSW aNonClientMetrics;
1487 aNonClientMetrics.cbSize = sizeof( aNonClientMetrics );
1488 if ( SystemParametersInfoW( SPI_GETNONCLIENTMETRICS, sizeof( aNonClientMetrics ), &aNonClientMetrics, 0 ) )
1490 LONG nFontHeight = aNonClientMetrics.lfMessageFont.lfHeight;
1491 if( nFontHeight < 0 )
1492 nFontHeight = -nFontHeight;
1494 if( aRect.GetHeight() && nFontHeight )
1496 aRect.AdjustBottom(aRect.GetHeight());
1497 aRect.AdjustBottom(nFontHeight);
1498 if( aRect.GetHeight() > aBoxRect.GetHeight() )
1499 aBoxRect.SetBottom( aBoxRect.Top() + aRect.GetHeight() );
1500 if( aRect.GetWidth() > aBoxRect.GetWidth() )
1501 aBoxRect.SetRight( aBoxRect.Left() + aRect.GetWidth() );
1502 rNativeContentRegion = aBoxRect;
1503 rNativeBoundingRegion = rNativeContentRegion;
1504 bRet = true;
1510 if( GetSalData()->mbThemeMenuSupport )
1512 if( nType == ControlType::MenuPopup )
1514 if( nPart == ControlPart::MenuItemCheckMark ||
1515 nPart == ControlPart::MenuItemRadioMark )
1517 HTHEME hTheme = getThemeHandle(mhWnd, L"Menu", mWinSalGraphicsImplBase);
1518 tools::Rectangle aBoxRect( rControlRegion );
1519 tools::Rectangle aRect( ImplGetThemeRect( hTheme, hDC,
1520 MENU_POPUPCHECK,
1521 MC_CHECKMARKNORMAL,
1522 aBoxRect ) );
1523 if (!aRect.IsEmpty())
1525 if (MARGINS mg;
1526 SUCCEEDED(GetThemeMargins(hTheme, hDC, MENU_POPUPCHECK, MC_CHECKMARKNORMAL,
1527 TMT_CONTENTMARGINS, nullptr, &mg)))
1529 aRect.AdjustLeft(-mg.cxLeftWidth);
1530 aRect.AdjustRight(mg.cxRightWidth);
1531 aRect.AdjustTop(-mg.cyTopHeight);
1532 aRect.AdjustBottom(mg.cyBottomHeight);
1534 rNativeContentRegion = rNativeBoundingRegion = aRect;
1535 bRet = true;
1541 if( nType == ControlType::Slider && ( (nPart == ControlPart::ThumbHorz) || (nPart == ControlPart::ThumbVert) ) )
1543 HTHEME hTheme = getThemeHandle(mhWnd, L"Trackbar", mWinSalGraphicsImplBase);
1544 if( hTheme )
1546 int iPart = (nPart == ControlPart::ThumbHorz) ? TKP_THUMB : TKP_THUMBVERT;
1547 int iState = (nPart == ControlPart::ThumbHorz) ? static_cast<int>(TUS_NORMAL) : static_cast<int>(TUVS_NORMAL);
1548 tools::Rectangle aThumbRect = ImplGetThemeRect( hTheme, hDC, iPart, iState, tools::Rectangle() );
1549 if( nPart == ControlPart::ThumbHorz )
1551 tools::Long nW = aThumbRect.GetWidth();
1552 tools::Rectangle aRect( rControlRegion );
1553 aRect.SetRight( aRect.Left() + nW - 1 );
1554 rNativeContentRegion = aRect;
1555 rNativeBoundingRegion = rNativeContentRegion;
1557 else
1559 tools::Long nH = aThumbRect.GetHeight();
1560 tools::Rectangle aRect( rControlRegion );
1561 aRect.SetBottom( aRect.Top() + nH - 1 );
1562 rNativeContentRegion = aRect;
1563 rNativeBoundingRegion = rNativeContentRegion;
1565 bRet = true;
1569 if ( ( nType == ControlType::TabItem ) && ( nPart == ControlPart::Entire ) )
1571 tools::Rectangle aControlRect( rControlRegion );
1572 rNativeContentRegion = aControlRect;
1574 aControlRect.AdjustBottom(-1);
1576 if( rControlValue.getType() == ControlType::TabItem )
1578 const TabitemValue& rValue = static_cast<const TabitemValue&>(rControlValue);
1579 if (rValue.isBothAligned())
1580 aControlRect.AdjustRight(-1);
1582 if ( nState & ControlState::SELECTED )
1584 aControlRect.AdjustLeft(-2);
1585 if (!rValue.isBothAligned())
1587 if (rValue.isLeftAligned() || rValue.isNotAligned())
1588 aControlRect.AdjustRight(2);
1589 if (rValue.isRightAligned())
1590 aControlRect.AdjustRight(1);
1592 aControlRect.AdjustTop(-2);
1593 aControlRect.AdjustBottom(2);
1596 rNativeBoundingRegion = aControlRect;
1597 bRet = true;
1600 ReleaseDC( mhWnd, hDC );
1601 return bRet;
1604 void WinSalGraphics::updateSettingsNative( AllSettings& rSettings )
1606 if ( !IsThemeActive() )
1607 return;
1609 StyleSettings aStyleSettings = rSettings.GetStyleSettings();
1610 ImplSVData* pSVData = ImplGetSVData();
1612 // don't draw frame around each and every toolbar
1613 pSVData->maNWFData.mbDockingAreaAvoidTBFrames = true;
1615 // FIXME get the color directly from the theme, not from the settings
1616 Color aMenuBarTextColor = aStyleSettings.GetPersonaMenuBarTextColor().value_or( aStyleSettings.GetMenuTextColor() );
1617 // in aero menuitem highlight text is drawn in the same color as normal
1618 // high contrast highlight color is not related to persona and not apply blur or transparency
1619 if( !aStyleSettings.GetHighContrastMode() )
1621 aStyleSettings.SetMenuHighlightTextColor( aStyleSettings.GetMenuTextColor() );
1622 aStyleSettings.SetMenuBarRolloverTextColor( aMenuBarTextColor );
1623 aStyleSettings.SetMenuBarHighlightTextColor( aMenuBarTextColor );
1626 pSVData->maNWFData.mnMenuFormatBorderX = 2;
1627 pSVData->maNWFData.mnMenuFormatBorderY = 2;
1628 pSVData->maNWFData.maMenuBarHighlightTextColor = aMenuBarTextColor;
1629 GetSalData()->mbThemeMenuSupport = true;
1631 rSettings.SetStyleSettings( aStyleSettings );
1634 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */