calc: on editing invalidation of view with different zoom is wrong
[LibreOffice.git] / vcl / win / gdi / salnativewidgets-luna.cxx
blobc837cb7a1c824e69f53f4839b125480c340d5d82
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>
44 #include <win/svsys.h>
45 #include <win/salgdi.h>
46 #include <win/saldata.hxx>
47 #include <win/salframe.h>
48 #include <win/scoped_gdi.hxx>
49 #include <win/wingdiimpl.hxx>
51 #include <uxtheme.h>
52 #include <vssym32.h>
54 #include <map>
55 #include <string>
56 #include <optional>
57 #include <ControlCacheKey.hxx>
59 typedef std::map< std::wstring, HTHEME > ThemeMap;
60 static ThemeMap aThemeMap;
62 /*********************************************************
63 * Initialize XP theming and local stuff
64 *********************************************************/
65 void SalData::initNWF()
67 ImplSVData* pSVData = ImplGetSVData();
69 // the menu bar and the top docking area should have a common background (gradient)
70 pSVData->maNWFData.mbMenuBarDockingAreaCommonBG = true;
73 // *********************************************************
74 // * Release theming handles
75 // ********************************************************
76 void SalData::deInitNWF()
78 for( auto& rEntry : aThemeMap )
79 CloseThemeData(rEntry.second);
80 aThemeMap.clear();
83 static HTHEME getThemeHandle(HWND hWnd, LPCWSTR name, WinSalGraphicsImplBase* pGraphicsImpl)
85 if( GetSalData()->mbThemeChanged )
87 // throw away invalid theme handles
88 SalData::deInitNWF();
89 // throw away native control cache
90 pGraphicsImpl->ClearNativeControlCache();
91 GetSalData()->mbThemeChanged = false;
94 ThemeMap::iterator iter;
95 if( (iter = aThemeMap.find( name )) != aThemeMap.end() )
96 return iter->second;
97 // theme not found -> add it to map
98 HTHEME hTheme = OpenThemeData( hWnd, name );
99 if( hTheme != nullptr )
100 aThemeMap[name] = hTheme;
101 return hTheme;
104 bool WinSalGraphics::isNativeControlSupported( ControlType nType, ControlPart nPart )
106 HTHEME hTheme = nullptr;
108 switch( nType )
110 case ControlType::Pushbutton:
111 case ControlType::Radiobutton:
112 case ControlType::Checkbox:
113 if( nPart == ControlPart::Entire )
114 hTheme = getThemeHandle(mhWnd, L"Button", mWinSalGraphicsImplBase);
115 break;
116 case ControlType::Scrollbar:
117 if( nPart == ControlPart::DrawBackgroundHorz || nPart == ControlPart::DrawBackgroundVert )
118 return false; // no background painting needed
119 if( nPart == ControlPart::Entire )
120 hTheme = getThemeHandle(mhWnd, L"Scrollbar", mWinSalGraphicsImplBase);
121 break;
122 case ControlType::Combobox:
123 if( nPart == ControlPart::HasBackgroundTexture )
124 return false; // we do not paint the inner part (ie the selection background/focus indication)
125 if( nPart == ControlPart::Entire )
126 hTheme = getThemeHandle(mhWnd, L"Edit", mWinSalGraphicsImplBase);
127 else if( nPart == ControlPart::ButtonDown )
128 hTheme = getThemeHandle(mhWnd, L"Combobox", mWinSalGraphicsImplBase);
129 break;
130 case ControlType::Spinbox:
131 if( nPart == ControlPart::Entire )
132 hTheme = getThemeHandle(mhWnd, L"Edit", mWinSalGraphicsImplBase);
133 else if( nPart == ControlPart::AllButtons ||
134 nPart == ControlPart::ButtonUp || nPart == ControlPart::ButtonDown ||
135 nPart == ControlPart::ButtonLeft|| nPart == ControlPart::ButtonRight )
136 hTheme = getThemeHandle(mhWnd, L"Spin", mWinSalGraphicsImplBase);
137 break;
138 case ControlType::SpinButtons:
139 if( nPart == ControlPart::Entire || nPart == ControlPart::AllButtons )
140 hTheme = getThemeHandle(mhWnd, L"Spin", mWinSalGraphicsImplBase);
141 break;
142 case ControlType::Editbox:
143 case ControlType::MultilineEditbox:
144 if( nPart == ControlPart::HasBackgroundTexture )
145 return false; // we do not paint the inner part (ie the selection background/focus indication)
146 //return TRUE;
147 if( nPart == ControlPart::Entire )
148 hTheme = getThemeHandle(mhWnd, L"Edit", mWinSalGraphicsImplBase);
149 break;
150 case ControlType::Listbox:
151 if( nPart == ControlPart::HasBackgroundTexture )
152 return false; // we do not paint the inner part (ie the selection background/focus indication)
153 if( nPart == ControlPart::Entire || nPart == ControlPart::ListboxWindow )
154 hTheme = getThemeHandle(mhWnd, L"Listview", mWinSalGraphicsImplBase);
155 else if( nPart == ControlPart::ButtonDown )
156 hTheme = getThemeHandle(mhWnd, L"Combobox", mWinSalGraphicsImplBase);
157 break;
158 case ControlType::TabPane:
159 case ControlType::TabBody:
160 case ControlType::TabItem:
161 if( nPart == ControlPart::Entire )
162 hTheme = getThemeHandle(mhWnd, L"Tab", mWinSalGraphicsImplBase);
163 break;
164 case ControlType::Toolbar:
165 if( nPart == ControlPart::Entire || nPart == ControlPart::Button )
166 hTheme = getThemeHandle(mhWnd, L"Toolbar", mWinSalGraphicsImplBase);
167 else
168 // use rebar theme for grip and background
169 hTheme = getThemeHandle(mhWnd, L"Rebar", mWinSalGraphicsImplBase);
170 break;
171 case ControlType::Menubar:
172 if( nPart == ControlPart::Entire )
173 hTheme = getThemeHandle(mhWnd, L"Rebar", mWinSalGraphicsImplBase);
174 else if( GetSalData()->mbThemeMenuSupport )
176 if( nPart == ControlPart::MenuItem )
177 hTheme = getThemeHandle(mhWnd, L"Menu", mWinSalGraphicsImplBase);
179 break;
180 case ControlType::MenuPopup:
181 if( GetSalData()->mbThemeMenuSupport )
183 if( nPart == ControlPart::Entire ||
184 nPart == ControlPart::MenuItem ||
185 nPart == ControlPart::MenuItemCheckMark ||
186 nPart == ControlPart::MenuItemRadioMark ||
187 nPart == ControlPart::Separator )
188 hTheme = getThemeHandle(mhWnd, L"Menu", mWinSalGraphicsImplBase);
190 break;
191 case ControlType::Progress:
192 if( nPart == ControlPart::Entire )
193 hTheme = getThemeHandle(mhWnd, L"Progress", mWinSalGraphicsImplBase);
194 break;
195 case ControlType::Slider:
196 if( nPart == ControlPart::TrackHorzArea || nPart == ControlPart::TrackVertArea )
197 hTheme = getThemeHandle(mhWnd, L"Trackbar", mWinSalGraphicsImplBase);
198 break;
199 case ControlType::ListNode:
200 if( nPart == ControlPart::Entire )
201 hTheme = getThemeHandle(mhWnd, L"TreeView", mWinSalGraphicsImplBase);
202 break;
203 default:
204 hTheme = nullptr;
205 break;
208 return (hTheme != nullptr);
211 bool WinSalGraphics::hitTestNativeControl( ControlType,
212 ControlPart,
213 const tools::Rectangle&,
214 const Point&,
215 bool& )
217 return false;
220 static bool ImplDrawTheme( HTHEME hTheme, HDC hDC, int iPart, int iState, RECT rc, const OUString& aStr)
222 HRESULT hr = DrawThemeBackground( hTheme, hDC, iPart, iState, &rc, nullptr);
224 if( aStr.getLength() )
226 RECT rcContent;
227 hr = GetThemeBackgroundContentRect( hTheme, hDC, iPart, iState, &rc, &rcContent);
228 hr = DrawThemeText( hTheme, hDC, iPart, iState,
229 o3tl::toW(aStr.getStr()), -1,
230 DT_CENTER | DT_VCENTER | DT_SINGLELINE,
231 0, &rcContent);
233 return (hr == S_OK);
236 // TS_TRUE returns optimal size
237 static std::optional<Size> ImplGetThemeSize(HTHEME hTheme, HDC hDC, int iPart, int iState, LPCRECT pRect, THEMESIZE eTS = TS_TRUE)
239 if (SIZE aSz; SUCCEEDED(GetThemePartSize(hTheme, hDC, iPart, iState, pRect, eTS, &aSz)))
240 return Size(aSz.cx, aSz.cy);
241 return {};
244 static tools::Rectangle ImplGetThemeRect( HTHEME hTheme, HDC hDC, int iPart, int iState, const tools::Rectangle& /* aRect */, THEMESIZE eTS = TS_TRUE )
246 if (const std::optional<Size> oSz = ImplGetThemeSize(hTheme, hDC, iPart, iState, nullptr, eTS))
247 return tools::Rectangle( 0, 0, oSz->Width(), oSz->Height() );
248 else
249 return tools::Rectangle();
252 // Helper functions
254 static void ImplConvertSpinbuttonValues( ControlPart nControlPart, const ControlState& rState, const tools::Rectangle& rRect,
255 int* pLunaPart, int *pLunaState, RECT *pRect )
257 if( nControlPart == ControlPart::ButtonDown )
259 *pLunaPart = SPNP_DOWN;
260 if( rState & ControlState::PRESSED )
261 *pLunaState = DNS_PRESSED;
262 else if( !(rState & ControlState::ENABLED) )
263 *pLunaState = DNS_DISABLED;
264 else if( rState & ControlState::ROLLOVER )
265 *pLunaState = DNS_HOT;
266 else
267 *pLunaState = DNS_NORMAL;
269 if( nControlPart == ControlPart::ButtonUp )
271 *pLunaPart = SPNP_UP;
272 if( rState & ControlState::PRESSED )
273 *pLunaState = UPS_PRESSED;
274 else if( !(rState & ControlState::ENABLED) )
275 *pLunaState = UPS_DISABLED;
276 else if( rState & ControlState::ROLLOVER )
277 *pLunaState = UPS_HOT;
278 else
279 *pLunaState = UPS_NORMAL;
281 if( nControlPart == ControlPart::ButtonRight )
283 *pLunaPart = SPNP_UPHORZ;
284 if( rState & ControlState::PRESSED )
285 *pLunaState = DNHZS_PRESSED;
286 else if( !(rState & ControlState::ENABLED) )
287 *pLunaState = DNHZS_DISABLED;
288 else if( rState & ControlState::ROLLOVER )
289 *pLunaState = DNHZS_HOT;
290 else
291 *pLunaState = DNHZS_NORMAL;
293 if( nControlPart == ControlPart::ButtonLeft )
295 *pLunaPart = SPNP_DOWNHORZ;
296 if( rState & ControlState::PRESSED )
297 *pLunaState = UPHZS_PRESSED;
298 else if( !(rState & ControlState::ENABLED) )
299 *pLunaState = UPHZS_DISABLED;
300 else if( rState & ControlState::ROLLOVER )
301 *pLunaState = UPHZS_HOT;
302 else
303 *pLunaState = UPHZS_NORMAL;
306 pRect->left = rRect.Left();
307 pRect->right = rRect.Right()+1;
308 pRect->top = rRect.Top();
309 pRect->bottom = rRect.Bottom()+1;
312 /// Draw an own toolbar style on Windows Vista or later, looks better there
313 static void impl_drawAeroToolbar( HDC hDC, RECT rc, bool bHorizontal )
315 if ( rc.top == 0 && bHorizontal )
317 const int GRADIENT_HEIGHT = 32;
319 LONG gradient_break = rc.top;
320 LONG gradient_bottom = rc.bottom - 1;
321 GRADIENT_RECT g_rect[1] = { { 0, 1 } };
323 // very slow gradient at the top (if we have space for that)
324 if ( gradient_bottom - rc.top > GRADIENT_HEIGHT )
326 gradient_break = gradient_bottom - GRADIENT_HEIGHT;
328 TRIVERTEX vert[2] = {
329 { rc.left, rc.top, 0xff00, 0xff00, 0xff00, 0xff00 },
330 { rc.right, gradient_break, 0xfa00, 0xfa00, 0xfa00, 0xff00 },
332 GdiGradientFill( hDC, vert, 2, g_rect, 1, GRADIENT_FILL_RECT_V );
335 // gradient at the bottom
336 TRIVERTEX vert[2] = {
337 { rc.left, gradient_break, 0xfa00, 0xfa00, 0xfa00, 0xff00 },
338 { rc.right, gradient_bottom, 0xf000, 0xf000, 0xf000, 0xff00 }
340 GdiGradientFill( hDC, vert, 2, g_rect, 1, GRADIENT_FILL_RECT_V );
342 // and a darker horizontal line under that
343 ScopedSelectedHPEN hPen(hDC, CreatePen(PS_SOLID, 1, RGB( 0xb0, 0xb0, 0xb0)));
345 MoveToEx( hDC, rc.left, gradient_bottom, nullptr );
346 LineTo( hDC, rc.right, gradient_bottom );
348 else
350 ScopedHBRUSH hbrush(CreateSolidBrush(RGB(0xf0, 0xf0, 0xf0)));
351 FillRect(hDC, &rc, hbrush.get());
353 // darker line to distinguish the toolbar and viewshell
354 // it is drawn only for the horizontal toolbars; it did not look well
355 // when done for the vertical ones too
356 if ( bHorizontal )
358 LONG from_x, from_y, to_x, to_y;
360 from_x = rc.left;
361 to_x = rc.right;
362 from_y = to_y = rc.top;
364 ScopedSelectedHPEN hPen(hDC, CreatePen(PS_SOLID, 1, RGB( 0xb0, 0xb0, 0xb0)));
366 MoveToEx( hDC, from_x, from_y, nullptr );
367 LineTo( hDC, to_x, to_y );
372 static bool implDrawNativeMenuMark(HDC hDC, HTHEME hTheme, RECT rc, ControlPart nPart,
373 ControlState nState, OUString const& aCaption)
375 int iState = (nState & ControlState::ENABLED) ? MCB_NORMAL : MCB_DISABLED;
376 ImplDrawTheme(hTheme, hDC, MENU_POPUPCHECKBACKGROUND, iState, rc, aCaption);
377 if (nPart == ControlPart::MenuItemCheckMark)
378 iState = (nState & ControlState::ENABLED) ? MC_CHECKMARKNORMAL : MC_CHECKMARKDISABLED;
379 else
380 iState = (nState & ControlState::ENABLED) ? MC_BULLETNORMAL : MC_BULLETDISABLED;
381 // tdf#133697: Get true size of mark, to avoid stretching
382 if (auto oSize = ImplGetThemeSize(hTheme, hDC, MENU_POPUPCHECK, iState, &rc))
384 // center the mark inside the passed rectangle
385 if (const auto dx = (rc.right - rc.left - oSize->Width() + 1) / 2; dx > 0)
387 rc.left += dx;
388 rc.right = rc.left + oSize->Width();
390 if (const auto dy = (rc.bottom - rc.top - oSize->Height() + 1) / 2; dy > 0)
392 rc.top += dy;
393 rc.bottom = rc.top + oSize->Height();
396 return ImplDrawTheme(hTheme, hDC, MENU_POPUPCHECK, iState, rc, aCaption);
399 bool UseDarkMode()
401 static bool bOSSupportsDarkMode = OSSupportsDarkMode();
402 if (!bOSSupportsDarkMode)
403 return false;
405 bool bRet(false);
406 switch (MiscSettings::GetDarkMode())
408 case 0: // auto
409 default:
411 HINSTANCE hUxthemeLib = LoadLibraryExW(L"uxtheme.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
412 if (!hUxthemeLib)
413 return false;
415 typedef bool(WINAPI* ShouldAppsUseDarkMode_t)();
416 if (auto ShouldAppsUseDarkMode = reinterpret_cast<ShouldAppsUseDarkMode_t>(GetProcAddress(hUxthemeLib, MAKEINTRESOURCEA(132))))
417 bRet = ShouldAppsUseDarkMode();
419 FreeLibrary(hUxthemeLib);
420 break;
422 case 1: // light
423 bRet = false;
424 break;
425 case 2: // dark
426 bRet = true;
427 break;
430 return bRet;
433 static bool ImplDrawNativeControl( HDC hDC, HTHEME hTheme, RECT rc,
434 ControlType nType,
435 ControlPart nPart,
436 ControlState nState,
437 const ImplControlValue& aValue,
438 OUString const & aCaption,
439 bool bUseDarkMode )
441 // a listbox dropdown is actually a combobox dropdown
442 if( nType == ControlType::Listbox )
443 if( nPart == ControlPart::ButtonDown )
444 nType = ControlType::Combobox;
446 // draw entire combobox as a large edit box
447 if( nType == ControlType::Combobox )
448 if( nPart == ControlPart::Entire )
449 nType = ControlType::Editbox;
451 // draw entire spinbox as a large edit box
452 if( nType == ControlType::Spinbox )
453 if( nPart == ControlPart::Entire )
454 nType = ControlType::Editbox;
456 int iPart(0), iState(0);
457 if( nType == ControlType::Scrollbar )
459 HRESULT hr;
460 if( nPart == ControlPart::ButtonUp )
462 iPart = SBP_ARROWBTN;
463 if( nState & ControlState::PRESSED )
464 iState = ABS_UPPRESSED;
465 else if( !(nState & ControlState::ENABLED) )
466 iState = ABS_UPDISABLED;
467 else if( nState & ControlState::ROLLOVER )
468 iState = ABS_UPHOT;
469 else
470 iState = ABS_UPNORMAL;
471 hr = DrawThemeBackground( hTheme, hDC, iPart, iState, &rc, nullptr);
472 return (hr == S_OK);
474 if( nPart == ControlPart::ButtonDown )
476 iPart = SBP_ARROWBTN;
477 if( nState & ControlState::PRESSED )
478 iState = ABS_DOWNPRESSED;
479 else if( !(nState & ControlState::ENABLED) )
480 iState = ABS_DOWNDISABLED;
481 else if( nState & ControlState::ROLLOVER )
482 iState = ABS_DOWNHOT;
483 else
484 iState = ABS_DOWNNORMAL;
485 hr = DrawThemeBackground( hTheme, hDC, iPart, iState, &rc, nullptr);
486 return (hr == S_OK);
488 if( nPart == ControlPart::ButtonLeft )
490 iPart = SBP_ARROWBTN;
491 if( nState & ControlState::PRESSED )
492 iState = ABS_LEFTPRESSED;
493 else if( !(nState & ControlState::ENABLED) )
494 iState = ABS_LEFTDISABLED;
495 else if( nState & ControlState::ROLLOVER )
496 iState = ABS_LEFTHOT;
497 else
498 iState = ABS_LEFTNORMAL;
499 hr = DrawThemeBackground( hTheme, hDC, iPart, iState, &rc, nullptr);
500 return (hr == S_OK);
502 if( nPart == ControlPart::ButtonRight )
504 iPart = SBP_ARROWBTN;
505 if( nState & ControlState::PRESSED )
506 iState = ABS_RIGHTPRESSED;
507 else if( !(nState & ControlState::ENABLED) )
508 iState = ABS_RIGHTDISABLED;
509 else if( nState & ControlState::ROLLOVER )
510 iState = ABS_RIGHTHOT;
511 else
512 iState = ABS_RIGHTNORMAL;
513 hr = DrawThemeBackground( hTheme, hDC, iPart, iState, &rc, nullptr);
514 return (hr == S_OK);
516 if( nPart == ControlPart::ThumbHorz || nPart == ControlPart::ThumbVert )
518 iPart = (nPart == ControlPart::ThumbHorz) ? SBP_THUMBBTNHORZ : SBP_THUMBBTNVERT;
519 if( nState & ControlState::PRESSED )
520 iState = SCRBS_PRESSED;
521 else if( !(nState & ControlState::ENABLED) )
522 iState = SCRBS_DISABLED;
523 else if( nState & ControlState::ROLLOVER )
524 iState = SCRBS_HOT;
525 else
526 iState = SCRBS_NORMAL;
528 SIZE sz;
529 GetThemePartSize(hTheme, hDC, iPart, iState, nullptr, TS_MIN, &sz);
530 GetThemePartSize(hTheme, hDC, iPart, iState, nullptr, TS_TRUE, &sz);
531 GetThemePartSize(hTheme, hDC, iPart, iState, nullptr, TS_DRAW, &sz);
533 hr = DrawThemeBackground( hTheme, hDC, iPart, iState, &rc, nullptr);
534 // paint gripper on thumb if enough space
535 if( ( (nPart == ControlPart::ThumbVert) && (rc.bottom-rc.top > 12) ) ||
536 ( (nPart == ControlPart::ThumbHorz) && (rc.right-rc.left > 12) ) )
538 iPart = (nPart == ControlPart::ThumbHorz) ? SBP_GRIPPERHORZ : SBP_GRIPPERVERT;
539 iState = 0;
540 DrawThemeBackground( hTheme, hDC, iPart, iState, &rc, nullptr);
542 return (hr == S_OK);
544 if( nPart == ControlPart::TrackHorzLeft || nPart == ControlPart::TrackHorzRight || nPart == ControlPart::TrackVertUpper || nPart == ControlPart::TrackVertLower )
546 switch( nPart )
548 case ControlPart::TrackHorzLeft: iPart = SBP_UPPERTRACKHORZ; break;
549 case ControlPart::TrackHorzRight: iPart = SBP_LOWERTRACKHORZ; break;
550 case ControlPart::TrackVertUpper: iPart = SBP_UPPERTRACKVERT; break;
551 case ControlPart::TrackVertLower: iPart = SBP_LOWERTRACKVERT; break;
552 default: break;
555 if( nState & ControlState::PRESSED )
556 iState = SCRBS_PRESSED;
557 else if( !(nState & ControlState::ENABLED) )
558 iState = SCRBS_DISABLED;
559 else if( nState & ControlState::ROLLOVER )
560 iState = SCRBS_HOT;
561 else
562 iState = SCRBS_NORMAL;
563 hr = DrawThemeBackground( hTheme, hDC, iPart, iState, &rc, nullptr);
564 return (hr == S_OK);
567 if( nType == ControlType::SpinButtons && nPart == ControlPart::AllButtons )
569 if( aValue.getType() == ControlType::SpinButtons )
571 const SpinbuttonValue* pValue = (aValue.getType() == ControlType::SpinButtons) ? static_cast<const SpinbuttonValue*>(&aValue) : nullptr;
573 RECT rect;
574 ImplConvertSpinbuttonValues( pValue->mnUpperPart, pValue->mnUpperState, pValue->maUpperRect, &iPart, &iState, &rect );
575 bool bOk = ImplDrawTheme( hTheme, hDC, iPart, iState, rect, aCaption);
577 if( bOk )
579 ImplConvertSpinbuttonValues( pValue->mnLowerPart, pValue->mnLowerState, pValue->maLowerRect, &iPart, &iState, &rect );
580 bOk = ImplDrawTheme( hTheme, hDC, iPart, iState, rect, aCaption);
583 return bOk;
586 if( nType == ControlType::Spinbox )
588 if( nPart == ControlPart::AllButtons )
590 if( aValue.getType() == ControlType::SpinButtons )
592 const SpinbuttonValue *pValue = static_cast<const SpinbuttonValue*>(&aValue);
594 RECT rect;
595 ImplConvertSpinbuttonValues( pValue->mnUpperPart, pValue->mnUpperState, pValue->maUpperRect, &iPart, &iState, &rect );
596 bool bOk = ImplDrawTheme( hTheme, hDC, iPart, iState, rect, aCaption);
598 if( bOk )
600 ImplConvertSpinbuttonValues( pValue->mnLowerPart, pValue->mnLowerState, pValue->maLowerRect, &iPart, &iState, &rect );
601 bOk = ImplDrawTheme( hTheme, hDC, iPart, iState, rect, aCaption);
604 return bOk;
608 if( nPart == ControlPart::ButtonDown )
610 iPart = SPNP_DOWN;
611 if( nState & ControlState::PRESSED )
612 iState = DNS_PRESSED;
613 else if( !(nState & ControlState::ENABLED) )
614 iState = DNS_DISABLED;
615 else if( nState & ControlState::ROLLOVER )
616 iState = DNS_HOT;
617 else
618 iState = DNS_NORMAL;
620 if( nPart == ControlPart::ButtonUp )
622 iPart = SPNP_UP;
623 if( nState & ControlState::PRESSED )
624 iState = UPS_PRESSED;
625 else if( !(nState & ControlState::ENABLED) )
626 iState = UPS_DISABLED;
627 else if( nState & ControlState::ROLLOVER )
628 iState = UPS_HOT;
629 else
630 iState = UPS_NORMAL;
632 if( nPart == ControlPart::ButtonRight )
634 iPart = SPNP_DOWNHORZ;
635 if( nState & ControlState::PRESSED )
636 iState = DNHZS_PRESSED;
637 else if( !(nState & ControlState::ENABLED) )
638 iState = DNHZS_DISABLED;
639 else if( nState & ControlState::ROLLOVER )
640 iState = DNHZS_HOT;
641 else
642 iState = DNHZS_NORMAL;
644 if( nPart == ControlPart::ButtonLeft )
646 iPart = SPNP_UPHORZ;
647 if( nState & ControlState::PRESSED )
648 iState = UPHZS_PRESSED;
649 else if( !(nState & ControlState::ENABLED) )
650 iState = UPHZS_DISABLED;
651 else if( nState & ControlState::ROLLOVER )
652 iState = UPHZS_HOT;
653 else
654 iState = UPHZS_NORMAL;
656 if( nPart == ControlPart::ButtonLeft || nPart == ControlPart::ButtonRight || nPart == ControlPart::ButtonUp || nPart == ControlPart::ButtonDown )
657 return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
659 if( nType == ControlType::Combobox )
661 if( nPart == ControlPart::ButtonDown )
663 iPart = CP_DROPDOWNBUTTON;
664 if( nState & ControlState::PRESSED )
665 iState = CBXS_PRESSED;
666 else if( !(nState & ControlState::ENABLED) )
667 iState = CBXS_DISABLED;
668 else if( nState & ControlState::ROLLOVER )
669 iState = CBXS_HOT;
670 else
671 iState = CBXS_NORMAL;
672 return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
675 if( nType == ControlType::Pushbutton )
677 iPart = BP_PUSHBUTTON;
678 if( nState & ControlState::PRESSED )
679 iState = PBS_PRESSED;
680 else if( !(nState & ControlState::ENABLED) )
681 iState = PBS_DISABLED;
682 else if( nState & ControlState::ROLLOVER )
683 iState = PBS_HOT;
684 else if( nState & ControlState::DEFAULT )
685 iState = PBS_DEFAULTED;
686 //else if( nState & ControlState::FOCUSED )
687 // iState = PBS_DEFAULTED; // may need to draw focus rect
688 else
689 iState = PBS_NORMAL;
691 return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
694 if( nType == ControlType::Radiobutton )
696 iPart = BP_RADIOBUTTON;
697 bool bChecked = ( aValue.getTristateVal() == ButtonValue::On );
699 if( nState & ControlState::PRESSED )
700 iState = bChecked ? RBS_CHECKEDPRESSED : RBS_UNCHECKEDPRESSED;
701 else if( !(nState & ControlState::ENABLED) )
702 iState = bChecked ? RBS_CHECKEDDISABLED : RBS_UNCHECKEDDISABLED;
703 else if( nState & ControlState::ROLLOVER )
704 iState = bChecked ? RBS_CHECKEDHOT : RBS_UNCHECKEDHOT;
705 else
706 iState = bChecked ? RBS_CHECKEDNORMAL : RBS_UNCHECKEDNORMAL;
708 //if( nState & ControlState::FOCUSED )
709 // iState |= PBS_DEFAULTED; // may need to draw focus rect
711 return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
714 if( nType == ControlType::Checkbox )
716 iPart = BP_CHECKBOX;
717 ButtonValue v = aValue.getTristateVal();
719 if( nState & ControlState::PRESSED )
720 iState = (v == ButtonValue::On) ? CBS_CHECKEDPRESSED :
721 ( (v == ButtonValue::Off) ? CBS_UNCHECKEDPRESSED : CBS_MIXEDPRESSED );
722 else if( !(nState & ControlState::ENABLED) )
723 iState = (v == ButtonValue::On) ? CBS_CHECKEDDISABLED :
724 ( (v == ButtonValue::Off) ? CBS_UNCHECKEDDISABLED : CBS_MIXEDDISABLED );
725 else if( nState & ControlState::ROLLOVER )
726 iState = (v == ButtonValue::On) ? CBS_CHECKEDHOT :
727 ( (v == ButtonValue::Off) ? CBS_UNCHECKEDHOT : CBS_MIXEDHOT );
728 else
729 iState = (v == ButtonValue::On) ? CBS_CHECKEDNORMAL :
730 ( (v == ButtonValue::Off) ? CBS_UNCHECKEDNORMAL : CBS_MIXEDNORMAL );
732 //if( nState & ControlState::FOCUSED )
733 // iState |= PBS_DEFAULTED; // may need to draw focus rect
735 //SIZE sz;
736 //THEMESIZE eSize = TS_DRAW; // TS_MIN, TS_TRUE, TS_DRAW
737 //GetThemePartSize( hTheme, hDC, iPart, iState, &rc, eSize, &sz);
739 return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
742 if (nType == ControlType::Editbox)
744 iPart = EP_EDITBORDER_NOSCROLL;
745 if( !(nState & ControlState::ENABLED) )
746 iState = EPSN_DISABLED;
747 else if( nState & ControlState::FOCUSED )
748 iState = EPSN_FOCUSED;
749 else if( nState & ControlState::ROLLOVER )
750 iState = EPSN_HOT;
751 else
752 iState = EPSN_NORMAL;
754 return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
757 if (nType == ControlType::MultilineEditbox)
759 iPart = EP_EDITTEXT;
760 if( !(nState & ControlState::ENABLED) )
761 iState = ETS_DISABLED;
762 else if( nState & ControlState::FOCUSED )
763 iState = ETS_FOCUSED;
764 else if( nState & ControlState::ROLLOVER )
765 iState = ETS_HOT;
766 else
767 iState = ETS_NORMAL;
769 return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
772 if( nType == ControlType::Listbox )
774 if( nPart == ControlPart::Entire || nPart == ControlPart::ListboxWindow )
776 iPart = LVP_EMPTYTEXT; // ??? no idea which part to choose here
777 return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
781 if( nType == ControlType::TabPane )
783 // tabpane in tabcontrols gets drawn in "darkmode" as if it was a
784 // a "light" theme, so bodge this by drawing a frame directly
785 if (bUseDarkMode)
787 Color aColor(Application::GetSettings().GetStyleSettings().GetDisableColor());
788 ScopedHBRUSH hbrush(CreateSolidBrush(RGB(aColor.GetRed(),
789 aColor.GetGreen(),
790 aColor.GetBlue())));
791 FrameRect(hDC, &rc, hbrush.get());
792 return true;
794 iPart = TABP_PANE;
795 return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
798 if( nType == ControlType::TabBody )
800 // tabbody in main window gets drawn in white in "darkmode", so bodge this here
801 if (bUseDarkMode)
803 Color aColor(Application::GetSettings().GetStyleSettings().GetWindowColor());
804 ScopedHBRUSH hbrush(CreateSolidBrush(RGB(aColor.GetRed(),
805 aColor.GetGreen(),
806 aColor.GetBlue())));
807 FillRect(hDC, &rc, hbrush.get());
808 return true;
811 iPart = TABP_BODY;
812 return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
815 if( nType == ControlType::TabItem )
817 iPart = TABP_TABITEMLEFTEDGE;
818 rc.bottom--;
820 OSL_ASSERT( aValue.getType() == ControlType::TabItem );
822 const TabitemValue& rValue = static_cast<const TabitemValue&>(aValue);
823 if (rValue.isBothAligned())
825 iPart = TABP_TABITEMLEFTEDGE;
826 rc.right--;
828 else if (rValue.isLeftAligned())
829 iPart = TABP_TABITEMLEFTEDGE;
830 else if (rValue.isRightAligned())
831 iPart = TABP_TABITEMRIGHTEDGE;
832 else
833 iPart = TABP_TABITEM;
835 if( !(nState & ControlState::ENABLED) )
836 iState = TILES_DISABLED;
837 else if( nState & ControlState::SELECTED )
839 iState = TILES_SELECTED;
840 // increase the selected tab
841 rc.left-=2;
842 if (rValue.isBothAligned())
844 if (rValue.isLeftAligned() || rValue.isNotAligned())
845 rc.right+=2;
846 if (rValue.isRightAligned())
847 rc.right+=1;
849 rc.top-=2;
850 rc.bottom+=2;
852 else if( nState & ControlState::ROLLOVER )
853 iState = TILES_HOT;
854 else if( nState & ControlState::FOCUSED )
855 iState = TILES_FOCUSED; // may need to draw focus rect
856 else
857 iState = TILES_NORMAL;
859 // tabitem in tabcontrols gets drawn in "darkmode" as if it was a
860 // a "light" theme, so bodge this by drawing with a button instead
861 if (bUseDarkMode)
863 Color aColor;
864 if (iState == TILES_SELECTED)
865 aColor = Application::GetSettings().GetStyleSettings().GetActiveTabColor();
866 else
867 aColor = Application::GetSettings().GetStyleSettings().GetInactiveTabColor();
868 ScopedHBRUSH hbrush(CreateSolidBrush(RGB(aColor.GetRed(),
869 aColor.GetGreen(),
870 aColor.GetBlue())));
871 FillRect(hDC, &rc, hbrush.get());
873 aColor = Application::GetSettings().GetStyleSettings().GetDisableColor();
874 ScopedSelectedHPEN hPen(hDC, CreatePen(PS_SOLID, 1, RGB(aColor.GetRed(),
875 aColor.GetGreen(),
876 aColor.GetBlue())));
877 POINT apt[4];
878 apt[0].x = rc.left;
879 apt[0].y = rc.bottom - (iPart == TABP_TABITEMLEFTEDGE ? 1 : 2);
880 apt[1].x = rc.left;
881 apt[1].y = rc.top;
882 apt[2].x = rc.right;
883 apt[2].y = rc.top;
884 apt[3].x = rc.right;
885 apt[3].y = rc.bottom - 1;
886 Polyline(hDC, apt, SAL_N_ELEMENTS(apt));
887 return true;
890 return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
893 if( nType == ControlType::Toolbar )
895 if( nPart == ControlPart::Button )
897 iPart = TP_BUTTON;
898 bool bChecked = ( aValue.getTristateVal() == ButtonValue::On );
899 if( !(nState & ControlState::ENABLED) )
900 //iState = TS_DISABLED;
901 // disabled buttons are typically not painted at all but we need visual
902 // feedback when travelling by keyboard over disabled entries
903 iState = TS_HOT;
904 else if( nState & ControlState::PRESSED )
905 iState = TS_PRESSED;
906 else if( nState & ControlState::ROLLOVER )
907 iState = bChecked ? TS_HOTCHECKED : TS_HOT;
908 else
909 iState = bChecked ? TS_CHECKED : TS_NORMAL;
910 return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
912 else if( nPart == ControlPart::ThumbHorz || nPart == ControlPart::ThumbVert )
914 // the vertical gripper is not supported in most themes and it makes no
915 // sense to only support horizontal gripper
916 //iPart = (nPart == ControlPart::ThumbHorz) ? RP_GRIPPERVERT : RP_GRIPPER;
917 //return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
919 else if( nPart == ControlPart::DrawBackgroundHorz || nPart == ControlPart::DrawBackgroundVert )
921 if( aValue.getType() == ControlType::Toolbar )
923 const ToolbarValue *pValue = static_cast<const ToolbarValue*>(&aValue);
924 if( pValue->mbIsTopDockingArea )
925 rc.top = 0; // extend potential gradient to cover menu bar as well
928 // toolbar in main window gets drawn in white in "darkmode", so bodge this here
929 if (bUseDarkMode)
931 Color aColor(Application::GetSettings().GetStyleSettings().GetWindowColor());
932 ScopedHBRUSH hbrush(CreateSolidBrush(RGB(aColor.GetRed(),
933 aColor.GetGreen(),
934 aColor.GetBlue())));
935 FillRect(hDC, &rc, hbrush.get());
936 return true;
939 // make it more compatible with Aero
940 if (ImplGetSVData()->maNWFData.mbDockingAreaAvoidTBFrames &&
941 !Application::GetSettings().GetStyleSettings().GetHighContrastMode())
943 impl_drawAeroToolbar( hDC, rc, nPart == ControlPart::DrawBackgroundHorz );
944 return true;
947 return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
951 if( nType == ControlType::Menubar )
953 if( nPart == ControlPart::Entire )
955 if( aValue.getType() == ControlType::Menubar )
957 const MenubarValue *pValue = static_cast<const MenubarValue*>(&aValue);
958 rc.bottom += pValue->maTopDockingAreaHeight; // extend potential gradient to cover docking area as well
960 // menubar in main window gets drawn in white in "darkmode", so bodge this here
961 if (bUseDarkMode)
963 Color aColor(Application::GetSettings().GetStyleSettings().GetWindowColor());
964 ScopedHBRUSH hbrush(CreateSolidBrush(RGB(aColor.GetRed(),
965 aColor.GetGreen(),
966 aColor.GetBlue())));
967 FillRect(hDC, &rc, hbrush.get());
968 return true;
971 // make it more compatible with Aero
972 if (ImplGetSVData()->maNWFData.mbDockingAreaAvoidTBFrames &&
973 !Application::GetSettings().GetStyleSettings().GetHighContrastMode())
975 impl_drawAeroToolbar( hDC, rc, true );
976 return true;
979 return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
981 else if( nPart == ControlPart::MenuItem )
983 if( nState & ControlState::ENABLED )
985 if( nState & ControlState::SELECTED )
986 iState = MBI_PUSHED;
987 else if( nState & ControlState::ROLLOVER )
988 iState = MBI_HOT;
989 else
990 iState = MBI_NORMAL;
992 else
994 if( nState & ControlState::SELECTED )
995 iState = MBI_DISABLEDPUSHED;
996 else if( nState & ControlState::ROLLOVER )
997 iState = MBI_DISABLEDHOT;
998 else
999 iState = MBI_DISABLED;
1001 return ImplDrawTheme( hTheme, hDC, MENU_BARITEM, iState, rc, aCaption );
1005 if( nType == ControlType::Progress )
1007 if( nPart != ControlPart::Entire )
1008 return false;
1010 if( ! ImplDrawTheme( hTheme, hDC, PP_BAR, iState, rc, aCaption) )
1011 return false;
1012 RECT aProgressRect = rc;
1013 if( GetThemeBackgroundContentRect( hTheme, hDC, PP_BAR, iState, &rc, &aProgressRect) != S_OK )
1014 return false;
1016 tools::Long nProgressWidth = aValue.getNumericVal();
1017 nProgressWidth *= (aProgressRect.right - aProgressRect.left);
1018 nProgressWidth /= (rc.right - rc.left);
1019 if( AllSettings::GetLayoutRTL() )
1020 aProgressRect.left = aProgressRect.right - nProgressWidth;
1021 else
1022 aProgressRect.right = aProgressRect.left + nProgressWidth;
1024 return ImplDrawTheme( hTheme, hDC, PP_CHUNK, iState, aProgressRect, aCaption );
1027 if( nType == ControlType::Slider )
1029 iPart = (nPart == ControlPart::TrackHorzArea) ? TKP_TRACK : TKP_TRACKVERT;
1030 iState = (nPart == ControlPart::TrackHorzArea) ? static_cast<int>(TRS_NORMAL) : static_cast<int>(TRVS_NORMAL);
1032 tools::Rectangle aTrackRect = ImplGetThemeRect( hTheme, hDC, iPart, iState, tools::Rectangle() );
1033 RECT aTRect = rc;
1034 if( nPart == ControlPart::TrackHorzArea )
1036 tools::Long nH = aTrackRect.GetHeight();
1037 aTRect.top += (rc.bottom - rc.top - nH)/2;
1038 aTRect.bottom = aTRect.top + nH;
1040 else
1042 tools::Long nW = aTrackRect.GetWidth();
1043 aTRect.left += (rc.right - rc.left - nW)/2;
1044 aTRect.right = aTRect.left + nW;
1046 ImplDrawTheme( hTheme, hDC, iPart, iState, aTRect, aCaption );
1048 RECT aThumbRect;
1049 OSL_ASSERT( aValue.getType() == ControlType::Slider );
1050 const SliderValue* pVal = static_cast<const SliderValue*>(&aValue);
1051 aThumbRect.left = pVal->maThumbRect.Left();
1052 aThumbRect.top = pVal->maThumbRect.Top();
1053 aThumbRect.right = pVal->maThumbRect.Right();
1054 aThumbRect.bottom = pVal->maThumbRect.Bottom();
1055 iPart = (nPart == ControlPart::TrackHorzArea) ? TKP_THUMB : TKP_THUMBVERT;
1056 iState = (nState & ControlState::ENABLED) ? TUS_NORMAL : TUS_DISABLED;
1057 return ImplDrawTheme( hTheme, hDC, iPart, iState, aThumbRect, aCaption );
1060 if( nType == ControlType::ListNode )
1062 if( nPart != ControlPart::Entire )
1063 return false;
1065 ButtonValue aButtonValue = aValue.getTristateVal();
1066 iPart = TVP_GLYPH;
1067 switch( aButtonValue )
1069 case ButtonValue::On:
1070 iState = GLPS_OPENED;
1071 break;
1072 case ButtonValue::Off:
1073 iState = GLPS_CLOSED;
1074 break;
1075 default:
1076 return false;
1078 return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption );
1081 if( GetSalData()->mbThemeMenuSupport )
1083 if( nType == ControlType::MenuPopup )
1085 if( nPart == ControlPart::Entire )
1087 RECT aGutterRC = rc;
1088 if( AllSettings::GetLayoutRTL() )
1090 aGutterRC.right -= aValue.getNumericVal()+1;
1091 aGutterRC.left = aGutterRC.right-3;
1093 else
1095 aGutterRC.left += aValue.getNumericVal();
1096 aGutterRC.right = aGutterRC.left+3;
1098 return
1099 ImplDrawTheme( hTheme, hDC, MENU_POPUPBACKGROUND, 0, rc, aCaption ) &&
1100 ImplDrawTheme( hTheme, hDC, MENU_POPUPGUTTER, 0, aGutterRC, aCaption )
1103 else if( nPart == ControlPart::MenuItem )
1105 if( nState & ControlState::ENABLED )
1106 iState = (nState & ControlState::SELECTED) ? MPI_HOT : MPI_NORMAL;
1107 else
1108 iState = (nState & ControlState::SELECTED) ? MPI_DISABLEDHOT : MPI_DISABLED;
1109 return ImplDrawTheme( hTheme, hDC, MENU_POPUPITEM, iState, rc, aCaption );
1111 else if( nPart == ControlPart::MenuItemCheckMark || nPart == ControlPart::MenuItemRadioMark )
1113 if (nState & ControlState::PRESSED)
1114 return implDrawNativeMenuMark(hDC, hTheme, rc, nPart, nState, aCaption);
1115 else
1116 return true; // unchecked: do nothing
1118 else if( nPart == ControlPart::Separator )
1120 // adjust for gutter position
1121 if( AllSettings::GetLayoutRTL() )
1122 rc.right -= aValue.getNumericVal()+1;
1123 else
1124 rc.left += aValue.getNumericVal()+1;
1125 tools::Rectangle aRect( ImplGetThemeRect( hTheme, hDC,
1126 MENU_POPUPSEPARATOR, 0, tools::Rectangle( rc.left, rc.top, rc.right, rc.bottom ) ) );
1127 // center the separator inside the passed rectangle
1128 auto const nDY = ((rc.bottom - rc.top + 1) - aRect.GetHeight()) / 2;
1129 rc.top += nDY;
1130 rc.bottom = rc.top+aRect.GetHeight()-1;
1131 return ImplDrawTheme( hTheme, hDC, MENU_POPUPSEPARATOR, 0, rc, aCaption );
1136 return false;
1139 bool WinSalGraphics::drawNativeControl( ControlType nType,
1140 ControlPart nPart,
1141 const tools::Rectangle& rControlRegion,
1142 ControlState nState,
1143 const ImplControlValue& aValue,
1144 const OUString& aCaption,
1145 const Color& /*rBackgroundColor*/ )
1147 bool bOk = false;
1148 HTHEME hTheme = nullptr;
1150 tools::Rectangle buttonRect = rControlRegion;
1151 tools::Rectangle cacheRect = rControlRegion;
1152 Size keySize = cacheRect.GetSize();
1154 WinSalGraphicsImplBase* pImpl = mWinSalGraphicsImplBase;
1155 if( !pImpl->UseRenderNativeControl())
1156 pImpl = nullptr;
1158 // tdf#95618 - A few controls render outside the region they're given.
1159 if (pImpl && nType == ControlType::TabItem)
1161 tools::Rectangle rNativeBoundingRegion;
1162 tools::Rectangle rNativeContentRegion;
1163 if (getNativeControlRegion(nType, nPart, rControlRegion, nState, aValue, aCaption,
1164 rNativeBoundingRegion, rNativeContentRegion))
1166 cacheRect = rNativeBoundingRegion;
1167 keySize = rNativeBoundingRegion.GetSize();
1172 ControlCacheKey aControlCacheKey(nType, nPart, nState, keySize);
1173 if (pImpl != nullptr && pImpl->TryRenderCachedNativeControl(aControlCacheKey, buttonRect.Left(), buttonRect.Top()))
1175 return true;
1178 const bool bUseDarkMode = UseDarkMode();
1179 if (bUseDarkMode)
1180 SetWindowTheme(mhWnd, L"Explorer", nullptr);
1182 switch( nType )
1184 case ControlType::Pushbutton:
1185 case ControlType::Radiobutton:
1186 case ControlType::Checkbox:
1187 hTheme = getThemeHandle(mhWnd, L"Button", mWinSalGraphicsImplBase);
1188 break;
1189 case ControlType::Scrollbar:
1190 if (bUseDarkMode)
1192 // tdf#153273 undo the earlier SetWindowTheme, and use an explicit Explorer::Scrollbar
1193 // a) with "Scrollbar" and SetWindowTheme(... "Explorer" ...) then scrollbars in dialog
1194 // and main windows are dark, but dropdowns are light
1195 // b) with "Explorer::Scrollbar" and SetWindowTheme(... "Explorer" ...) then scrollbars
1196 // in dropdowns are dark, but scrollbars in dialogs and main windows are sort of "extra
1197 // dark"
1198 // c) with "Explorer::Scrollbar" and no SetWindowTheme both cases are dark
1199 SetWindowTheme(mhWnd, nullptr, nullptr);
1200 hTheme = getThemeHandle(mhWnd, L"Explorer::Scrollbar", mWinSalGraphicsImplBase);
1202 else
1203 hTheme = getThemeHandle(mhWnd, L"Scrollbar", mWinSalGraphicsImplBase);
1204 break;
1205 case ControlType::Combobox:
1206 if( nPart == ControlPart::Entire )
1208 if (bUseDarkMode && !(nState & ControlState::FOCUSED))
1209 SetWindowTheme(mhWnd, L"CFD", nullptr);
1210 hTheme = getThemeHandle(mhWnd, L"Edit", mWinSalGraphicsImplBase);
1212 else if( nPart == ControlPart::ButtonDown )
1214 if (bUseDarkMode)
1215 SetWindowTheme(mhWnd, L"CFD", nullptr);
1216 hTheme = getThemeHandle(mhWnd, L"Combobox", mWinSalGraphicsImplBase);
1218 break;
1219 case ControlType::Spinbox:
1220 if( nPart == ControlPart::Entire )
1222 if (bUseDarkMode && !(nState & ControlState::FOCUSED))
1223 SetWindowTheme(mhWnd, L"CFD", nullptr);
1224 hTheme = getThemeHandle(mhWnd, L"Edit", mWinSalGraphicsImplBase);
1226 else
1227 hTheme = getThemeHandle(mhWnd, L"Spin", mWinSalGraphicsImplBase);
1228 break;
1229 case ControlType::SpinButtons:
1230 hTheme = getThemeHandle(mhWnd, L"Spin", mWinSalGraphicsImplBase);
1231 break;
1232 case ControlType::Editbox:
1233 if (bUseDarkMode && !(nState & ControlState::FOCUSED))
1234 SetWindowTheme(mhWnd, L"CFD", nullptr);
1235 hTheme = getThemeHandle(mhWnd, L"Edit", mWinSalGraphicsImplBase);
1236 break;
1237 case ControlType::MultilineEditbox:
1238 hTheme = getThemeHandle(mhWnd, L"Edit", mWinSalGraphicsImplBase);
1239 break;
1240 case ControlType::Listbox:
1241 if( nPart == ControlPart::Entire || nPart == ControlPart::ListboxWindow )
1242 hTheme = getThemeHandle(mhWnd, L"Listview", mWinSalGraphicsImplBase);
1243 else if( nPart == ControlPart::ButtonDown )
1245 if (bUseDarkMode)
1246 SetWindowTheme(mhWnd, L"CFD", nullptr);
1247 hTheme = getThemeHandle(mhWnd, L"Combobox", mWinSalGraphicsImplBase);
1249 break;
1250 case ControlType::TabBody:
1251 hTheme = getThemeHandle(mhWnd, L"Tab", mWinSalGraphicsImplBase);
1252 break;
1253 case ControlType::TabPane:
1254 case ControlType::TabItem:
1255 hTheme = getThemeHandle(mhWnd, L"Tab", mWinSalGraphicsImplBase);
1256 break;
1257 case ControlType::Toolbar:
1258 if( nPart == ControlPart::Entire || nPart == ControlPart::Button )
1259 hTheme = getThemeHandle(mhWnd, L"Toolbar", mWinSalGraphicsImplBase);
1260 else
1261 // use rebar for grip and background
1262 hTheme = getThemeHandle(mhWnd, L"Rebar", mWinSalGraphicsImplBase);
1263 break;
1264 case ControlType::Menubar:
1265 if( nPart == ControlPart::Entire )
1266 hTheme = getThemeHandle(mhWnd, L"Rebar", mWinSalGraphicsImplBase);
1267 else if( GetSalData()->mbThemeMenuSupport )
1269 if( nPart == ControlPart::MenuItem )
1270 hTheme = getThemeHandle(mhWnd, L"Menu", mWinSalGraphicsImplBase);
1272 break;
1273 case ControlType::Progress:
1274 if( nPart == ControlPart::Entire )
1275 hTheme = getThemeHandle(mhWnd, L"Progress", mWinSalGraphicsImplBase);
1276 break;
1277 case ControlType::ListNode:
1278 if( nPart == ControlPart::Entire )
1279 hTheme = getThemeHandle(mhWnd, L"TreeView", mWinSalGraphicsImplBase);
1280 break;
1281 case ControlType::Slider:
1282 if( nPart == ControlPart::TrackHorzArea || nPart == ControlPart::TrackVertArea )
1283 hTheme = getThemeHandle(mhWnd, L"Trackbar", mWinSalGraphicsImplBase);
1284 break;
1285 case ControlType::MenuPopup:
1286 if( GetSalData()->mbThemeMenuSupport )
1288 if( nPart == ControlPart::Entire || nPart == ControlPart::MenuItem ||
1289 nPart == ControlPart::MenuItemCheckMark || nPart == ControlPart::MenuItemRadioMark ||
1290 nPart == ControlPart::Separator
1292 hTheme = getThemeHandle(mhWnd, L"Menu", mWinSalGraphicsImplBase);
1294 break;
1295 default:
1296 hTheme = nullptr;
1297 break;
1300 if( !hTheme )
1302 if (bUseDarkMode)
1303 SetWindowTheme(mhWnd, nullptr, nullptr);
1304 return false;
1307 RECT rc;
1308 rc.left = buttonRect.Left();
1309 rc.right = buttonRect.Right()+1;
1310 rc.top = buttonRect.Top();
1311 rc.bottom = buttonRect.Bottom()+1;
1313 OUString aCaptionStr(aCaption.replace('~', '&')); // translate mnemonics
1315 if (pImpl == nullptr)
1317 // set default text alignment
1318 int ta = SetTextAlign(getHDC(), TA_LEFT|TA_TOP|TA_NOUPDATECP);
1320 bOk = ImplDrawNativeControl(getHDC(), hTheme, rc, nType, nPart, nState, aValue, aCaptionStr, bUseDarkMode);
1322 // restore alignment
1323 SetTextAlign(getHDC(), ta);
1325 else
1327 // We can do OpenGL/Skia
1328 std::unique_ptr<CompatibleDC> aBlackDC(CompatibleDC::create(*this, cacheRect.Left(), cacheRect.Top(), cacheRect.GetWidth()+1, cacheRect.GetHeight()+1));
1329 SetTextAlign(aBlackDC->getCompatibleHDC(), TA_LEFT|TA_TOP|TA_NOUPDATECP);
1330 aBlackDC->fill(RGB(0, 0, 0));
1332 std::unique_ptr<CompatibleDC> aWhiteDC(CompatibleDC::create(*this, cacheRect.Left(), cacheRect.Top(), cacheRect.GetWidth()+1, cacheRect.GetHeight()+1));
1333 SetTextAlign(aWhiteDC->getCompatibleHDC(), TA_LEFT|TA_TOP|TA_NOUPDATECP);
1334 aWhiteDC->fill(RGB(0xff, 0xff, 0xff));
1336 if (ImplDrawNativeControl(aBlackDC->getCompatibleHDC(), hTheme, rc, nType, nPart, nState, aValue, aCaptionStr, bUseDarkMode) &&
1337 ImplDrawNativeControl(aWhiteDC->getCompatibleHDC(), hTheme, rc, nType, nPart, nState, aValue, aCaptionStr, bUseDarkMode))
1339 bOk = pImpl->RenderAndCacheNativeControl(*aWhiteDC, *aBlackDC, cacheRect.Left(), cacheRect.Top(), aControlCacheKey);
1343 if (bUseDarkMode)
1344 SetWindowTheme(mhWnd, nullptr, nullptr);
1345 return bOk;
1348 bool WinSalGraphics::getNativeControlRegion( ControlType nType,
1349 ControlPart nPart,
1350 const tools::Rectangle& rControlRegion,
1351 ControlState nState,
1352 const ImplControlValue& rControlValue,
1353 const OUString&,
1354 tools::Rectangle &rNativeBoundingRegion,
1355 tools::Rectangle &rNativeContentRegion )
1357 bool bRet = false;
1359 // FIXME: rNativeBoundingRegion has a different origin
1360 // depending on which part is used; horrors.
1362 HDC hDC = GetDC( mhWnd );
1363 if( nType == ControlType::Toolbar )
1365 if( nPart == ControlPart::ThumbHorz || nPart == ControlPart::ThumbVert )
1368 // the vertical gripper is not supported in most themes and it makes no
1369 // sense to only support horizontal gripper
1371 HTHEME hTheme = getThemeHandle(mhWnd, L"Rebar", mWinSalGraphicsImplBase);
1372 if( hTheme )
1374 tools::Rectangle aRect( ImplGetThemeRect( hTheme, hDC, nPart == ControlPart::ThumbHorz ? RP_GRIPPERVERT : RP_GRIPPER,
1375 0, rControlRegion.GetBoundRect() ) );
1376 if( nPart == ControlPart::ThumbHorz && !aRect.IsEmpty() )
1378 tools::Rectangle aVertRect( 0, 0, aRect.getHeight(), aRect.getWidth() );
1379 rNativeContentRegion = aVertRect;
1381 else
1382 rNativeContentRegion = aRect;
1383 rNativeBoundingRegion = rNativeContentRegion;
1384 if( !rNativeContentRegion.IsEmpty() )
1385 bRet = TRUE;
1389 if( nPart == ControlPart::Button )
1391 HTHEME hTheme = getThemeHandle(mhWnd, L"Toolbar", mWinSalGraphicsImplBase);
1392 if( hTheme )
1394 tools::Rectangle aRect( ImplGetThemeRect( hTheme, hDC, TP_SPLITBUTTONDROPDOWN,
1395 TS_HOT, rControlRegion ) );
1396 rNativeContentRegion = aRect;
1397 rNativeBoundingRegion = rNativeContentRegion;
1398 if( !rNativeContentRegion.IsEmpty() )
1399 bRet = true;
1403 if( nType == ControlType::Progress && nPart == ControlPart::Entire )
1405 HTHEME hTheme = getThemeHandle(mhWnd, L"Progress", mWinSalGraphicsImplBase);
1406 if( hTheme )
1408 tools::Rectangle aRect( ImplGetThemeRect( hTheme, hDC, PP_BAR,
1409 0, rControlRegion ) );
1410 rNativeContentRegion = aRect;
1411 rNativeBoundingRegion = rNativeContentRegion;
1412 if( !rNativeContentRegion.IsEmpty() )
1413 bRet = true;
1416 if( (nType == ControlType::Listbox || nType == ControlType::Combobox ) && nPart == ControlPart::Entire )
1418 HTHEME hTheme = getThemeHandle(mhWnd, L"Combobox", mWinSalGraphicsImplBase);
1419 if( hTheme )
1421 tools::Rectangle aBoxRect( rControlRegion );
1422 tools::Rectangle aRect( ImplGetThemeRect( hTheme, hDC, CP_DROPDOWNBUTTON,
1423 CBXS_NORMAL, aBoxRect ) );
1424 if( aRect.GetHeight() > aBoxRect.GetHeight() )
1425 aBoxRect.SetBottom( aBoxRect.Top() + aRect.GetHeight() );
1426 if( aRect.GetWidth() > aBoxRect.GetWidth() )
1427 aBoxRect.SetRight( aBoxRect.Left() + aRect.GetWidth() );
1428 rNativeContentRegion = aBoxRect;
1429 rNativeBoundingRegion = rNativeContentRegion;
1430 if( !aRect.IsEmpty() )
1431 bRet = true;
1435 if( (nType == ControlType::Editbox || nType == ControlType::Spinbox) && nPart == ControlPart::Entire )
1437 HTHEME hTheme = getThemeHandle(mhWnd, L"Edit", mWinSalGraphicsImplBase);
1438 if( hTheme )
1440 // get border size
1441 tools::Rectangle aBoxRect( rControlRegion );
1442 tools::Rectangle aRect( ImplGetThemeRect( hTheme, hDC, EP_BACKGROUNDWITHBORDER,
1443 EBWBS_HOT, aBoxRect ) );
1444 // ad app font height
1445 NONCLIENTMETRICSW aNonClientMetrics;
1446 aNonClientMetrics.cbSize = sizeof( aNonClientMetrics );
1447 if ( SystemParametersInfoW( SPI_GETNONCLIENTMETRICS, sizeof( aNonClientMetrics ), &aNonClientMetrics, 0 ) )
1449 LONG nFontHeight = aNonClientMetrics.lfMessageFont.lfHeight;
1450 if( nFontHeight < 0 )
1451 nFontHeight = -nFontHeight;
1453 if( aRect.GetHeight() && nFontHeight )
1455 aRect.AdjustBottom(aRect.GetHeight());
1456 aRect.AdjustBottom(nFontHeight);
1457 if( aRect.GetHeight() > aBoxRect.GetHeight() )
1458 aBoxRect.SetBottom( aBoxRect.Top() + aRect.GetHeight() );
1459 if( aRect.GetWidth() > aBoxRect.GetWidth() )
1460 aBoxRect.SetRight( aBoxRect.Left() + aRect.GetWidth() );
1461 rNativeContentRegion = aBoxRect;
1462 rNativeBoundingRegion = rNativeContentRegion;
1463 bRet = true;
1469 if( GetSalData()->mbThemeMenuSupport )
1471 if( nType == ControlType::MenuPopup )
1473 if( nPart == ControlPart::MenuItemCheckMark ||
1474 nPart == ControlPart::MenuItemRadioMark )
1476 HTHEME hTheme = getThemeHandle(mhWnd, L"Menu", mWinSalGraphicsImplBase);
1477 tools::Rectangle aBoxRect( rControlRegion );
1478 tools::Rectangle aRect( ImplGetThemeRect( hTheme, hDC,
1479 MENU_POPUPCHECK,
1480 MC_CHECKMARKNORMAL,
1481 aBoxRect ) );
1482 if (!aRect.IsEmpty())
1484 if (MARGINS mg;
1485 SUCCEEDED(GetThemeMargins(hTheme, hDC, MENU_POPUPCHECK, MC_CHECKMARKNORMAL,
1486 TMT_CONTENTMARGINS, nullptr, &mg)))
1488 aRect.AdjustLeft(-mg.cxLeftWidth);
1489 aRect.AdjustRight(mg.cxRightWidth);
1490 aRect.AdjustTop(-mg.cyTopHeight);
1491 aRect.AdjustBottom(mg.cyBottomHeight);
1493 rNativeContentRegion = rNativeBoundingRegion = aRect;
1494 bRet = true;
1500 if( nType == ControlType::Slider && ( (nPart == ControlPart::ThumbHorz) || (nPart == ControlPart::ThumbVert) ) )
1502 HTHEME hTheme = getThemeHandle(mhWnd, L"Trackbar", mWinSalGraphicsImplBase);
1503 if( hTheme )
1505 int iPart = (nPart == ControlPart::ThumbHorz) ? TKP_THUMB : TKP_THUMBVERT;
1506 int iState = (nPart == ControlPart::ThumbHorz) ? static_cast<int>(TUS_NORMAL) : static_cast<int>(TUVS_NORMAL);
1507 tools::Rectangle aThumbRect = ImplGetThemeRect( hTheme, hDC, iPart, iState, tools::Rectangle() );
1508 if( nPart == ControlPart::ThumbHorz )
1510 tools::Long nW = aThumbRect.GetWidth();
1511 tools::Rectangle aRect( rControlRegion );
1512 aRect.SetRight( aRect.Left() + nW - 1 );
1513 rNativeContentRegion = aRect;
1514 rNativeBoundingRegion = rNativeContentRegion;
1516 else
1518 tools::Long nH = aThumbRect.GetHeight();
1519 tools::Rectangle aRect( rControlRegion );
1520 aRect.SetBottom( aRect.Top() + nH - 1 );
1521 rNativeContentRegion = aRect;
1522 rNativeBoundingRegion = rNativeContentRegion;
1524 bRet = true;
1528 if ( ( nType == ControlType::TabItem ) && ( nPart == ControlPart::Entire ) )
1530 tools::Rectangle aControlRect( rControlRegion );
1531 rNativeContentRegion = aControlRect;
1533 aControlRect.AdjustBottom(-1);
1535 if( rControlValue.getType() == ControlType::TabItem )
1537 const TabitemValue& rValue = static_cast<const TabitemValue&>(rControlValue);
1538 if (rValue.isBothAligned())
1539 aControlRect.AdjustRight(-1);
1541 if ( nState & ControlState::SELECTED )
1543 aControlRect.AdjustLeft(-2);
1544 if (!rValue.isBothAligned())
1546 if (rValue.isLeftAligned() || rValue.isNotAligned())
1547 aControlRect.AdjustRight(2);
1548 if (rValue.isRightAligned())
1549 aControlRect.AdjustRight(1);
1551 aControlRect.AdjustTop(-2);
1552 aControlRect.AdjustBottom(2);
1555 rNativeBoundingRegion = aControlRect;
1556 bRet = true;
1559 ReleaseDC( mhWnd, hDC );
1560 return bRet;
1563 void WinSalGraphics::updateSettingsNative( AllSettings& rSettings )
1565 if ( !IsThemeActive() )
1566 return;
1568 StyleSettings aStyleSettings = rSettings.GetStyleSettings();
1569 ImplSVData* pSVData = ImplGetSVData();
1571 // don't draw frame around each and every toolbar
1572 pSVData->maNWFData.mbDockingAreaAvoidTBFrames = true;
1574 // FIXME get the color directly from the theme, not from the settings
1575 Color aMenuBarTextColor = aStyleSettings.GetPersonaMenuBarTextColor().value_or( aStyleSettings.GetMenuTextColor() );
1576 // in aero menuitem highlight text is drawn in the same color as normal
1577 aStyleSettings.SetMenuHighlightTextColor( aStyleSettings.GetMenuTextColor() );
1578 aStyleSettings.SetMenuBarRolloverTextColor( aMenuBarTextColor );
1579 aStyleSettings.SetMenuBarHighlightTextColor( aMenuBarTextColor );
1580 pSVData->maNWFData.mnMenuFormatBorderX = 2;
1581 pSVData->maNWFData.mnMenuFormatBorderY = 2;
1582 pSVData->maNWFData.maMenuBarHighlightTextColor = aMenuBarTextColor;
1583 GetSalData()->mbThemeMenuSupport = true;
1585 rSettings.SetStyleSettings( aStyleSettings );
1588 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */