bump product version to 7.6.3.2-android
[LibreOffice.git] / vcl / osx / salnativewidgets.cxx
blob46d13e7473c69c1875d4a3a6cbccd80fb7511e99
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 #include <config_features.h>
21 #include <tools/long.hxx>
22 #include <vcl/salnativewidgets.hxx>
23 #include <vcl/decoview.hxx>
24 #include <vcl/svapp.hxx>
25 #include <vcl/threadex.hxx>
26 #include <vcl/timer.hxx>
27 #include <vcl/settings.hxx>
29 #include <quartz/salgdi.h>
30 #include <osx/salnativewidgets.h>
31 #include <osx/saldata.hxx>
32 #include <osx/salframe.h>
34 #include <premac.h>
35 #include <Carbon/Carbon.h>
36 #include <postmac.h>
38 #include "cuidraw.hxx"
40 // presentation of native widgets consists of two important methods:
42 // AquaSalGraphics::getNativeControlRegion to determine native rectangle in pixels to draw the widget
43 // AquaSalGraphics::drawNativeControl to do the drawing operation itself
45 // getNativeControlRegion has to calculate a content rectangle within it is safe to draw the widget. Furthermore a bounding rectangle
46 // has to be calculated by getNativeControlRegion to consider adornments like a focus rectangle. As drawNativeControl uses Carbon
47 // API calls, all widgets are drawn without text. Drawing of text is done separately by VCL on top of graphical Carbon widget
48 // representation. drawNativeControl is called by VCL using content rectangle determined by getNativeControlRegion.
50 // FIXME: when calculation bounding rectangle larger then content rectangle, text displayed by VCL will become misaligned. To avoid
51 // misalignment bounding rectangle and content rectangle are calculated equally including adornments. Reduction of size for content
52 // is done by drawNativeControl subsequently. Only exception is editbox: As other widgets have distinct ControlPart::SubEdit control
53 // parts, editbox bounding rectangle and content rectangle are both calculated to reflect content area. Extending size for
54 // adornments is done by drawNativeControl subsequently.
56 #if !HAVE_FEATURE_MACOSX_SANDBOX
58 @interface NSWindow(CoreUIRendererPrivate)
59 + (CUIRendererRef)coreUIRenderer;
60 @end
62 #endif
64 static HIRect ImplGetHIRectFromRectangle(tools::Rectangle aRect)
66 HIRect aHIRect;
67 aHIRect.origin.x = static_cast<float>(aRect.Left());
68 aHIRect.origin.y = static_cast<float>(aRect.Top());
69 aHIRect.size.width = static_cast<float>(aRect.GetWidth());
70 aHIRect.size.height = static_cast<float>(aRect.GetHeight());
71 return aHIRect;
74 static NSControlStateValue ImplGetButtonValue(ButtonValue aButtonValue)
76 switch (aButtonValue)
78 case ButtonValue::On:
79 return NSControlStateValueOn;
80 case ButtonValue::Off:
81 case ButtonValue::DontKnow:
82 return NSControlStateValueOff;
83 case ButtonValue::Mixed:
84 default:
85 return NSControlStateValueMixed;
89 static bool AquaGetScrollRect(/* TODO: int nScreen, */
90 ControlPart nPart, const tools::Rectangle &rControlRect, tools::Rectangle &rResultRect)
92 bool bRetVal = true;
93 rResultRect = rControlRect;
94 switch (nPart)
96 case ControlPart::ButtonUp:
97 rResultRect.SetBottom(rResultRect.Top());
98 break;
99 case ControlPart::ButtonDown:
100 rResultRect.SetTop(rResultRect.Bottom());
101 break;
102 case ControlPart::ButtonLeft:
103 rResultRect.SetRight(rResultRect.Left());
104 break;
105 case ControlPart::ButtonRight:
106 rResultRect.SetLeft(rResultRect.Right());
107 break;
108 case ControlPart::TrackHorzArea:
109 case ControlPart::TrackVertArea:
110 case ControlPart::ThumbHorz:
111 case ControlPart::ThumbVert:
112 case ControlPart::TrackHorzLeft:
113 case ControlPart::TrackHorzRight:
114 case ControlPart::TrackVertUpper:
115 case ControlPart::TrackVertLower:
116 break;
117 default:
118 bRetVal = false;
120 return bRetVal;
123 bool AquaSalGraphics::isNativeControlSupported(ControlType nType, ControlPart nPart)
125 // native controls are now defaults. If you want to disable native controls, set the environment variable SAL_NO_NWF to
126 // something and VCL controls will be used as default again.
128 switch (nType)
130 case ControlType::Pushbutton:
131 case ControlType::Radiobutton:
132 case ControlType::Checkbox:
133 case ControlType::ListNode:
134 if (nPart == ControlPart::Entire)
135 return true;
136 break;
137 case ControlType::Scrollbar:
138 if (nPart == ControlPart::DrawBackgroundHorz || nPart == ControlPart::DrawBackgroundVert
139 || nPart == ControlPart::Entire || nPart == ControlPart::HasThreeButtons)
140 return true;
141 break;
142 case ControlType::Slider:
143 if (nPart == ControlPart::TrackHorzArea || nPart == ControlPart::TrackVertArea)
144 return true;
145 break;
146 case ControlType::Editbox:
147 if (nPart == ControlPart::Entire || nPart == ControlPart::HasBackgroundTexture)
148 return true;
149 break;
150 case ControlType::MultilineEditbox:
151 if (nPart == ControlPart::Entire || nPart == ControlPart::HasBackgroundTexture)
152 return true;
153 break;
154 case ControlType::Spinbox:
155 if (nPart == ControlPart::Entire || nPart == ControlPart::AllButtons || nPart == ControlPart::HasBackgroundTexture)
156 return true;
157 break;
158 case ControlType::SpinButtons:
159 return false;
160 case ControlType::Combobox:
161 if (nPart == ControlPart::Entire || nPart == ControlPart::HasBackgroundTexture)
162 return true;
163 break;
164 case ControlType::Listbox:
165 if (nPart == ControlPart::Entire || nPart == ControlPart::ListboxWindow || nPart == ControlPart::HasBackgroundTexture
166 || nPart == ControlPart::SubEdit)
167 return true;
168 break;
169 case ControlType::TabItem:
170 case ControlType::TabPane:
171 case ControlType::TabBody:
172 if (nPart == ControlPart::Entire || nPart == ControlPart::TabsDrawRtl || nPart == ControlPart::HasBackgroundTexture)
173 return true;
174 break;
175 case ControlType::Toolbar:
176 if (nPart == ControlPart::Entire || nPart == ControlPart::DrawBackgroundHorz
177 || nPart == ControlPart::DrawBackgroundVert)
178 return true;
179 break;
180 case ControlType::WindowBackground:
181 if (nPart == ControlPart::BackgroundWindow || nPart == ControlPart::BackgroundDialog)
182 return true;
183 break;
184 case ControlType::Menubar:
185 if (nPart == ControlPart::Entire)
186 return true;
187 break;
188 case ControlType::Tooltip:
189 if (nPart == ControlPart::Entire)
190 return true;
191 break;
192 case ControlType::MenuPopup:
193 if (nPart == ControlPart::Entire || nPart == ControlPart::MenuItem || nPart == ControlPart::MenuItemCheckMark
194 || nPart == ControlPart::MenuItemRadioMark)
195 return true;
196 break;
197 case ControlType::Progress:
198 case ControlType::IntroProgress:
199 if (nPart == ControlPart::Entire)
200 return true;
201 break;
202 case ControlType::Frame:
203 if (nPart == ControlPart::Border)
204 return true;
205 break;
206 case ControlType::ListNet:
207 if (nPart == ControlPart::Entire)
208 return true;
209 break;
210 default:
211 break;
213 return false;
216 bool AquaSalGraphics::hitTestNativeControl(ControlType nType, ControlPart nPart, const tools::Rectangle &rControlRegion,
217 const Point &rPos, bool& rIsInside)
219 if (nType == ControlType::Scrollbar)
221 tools::Rectangle aRect;
222 bool bValid = AquaGetScrollRect(/* TODO: int nScreen, */
223 nPart, rControlRegion, aRect);
224 rIsInside = bValid && aRect.Contains(rPos);
225 return bValid;
227 return false;
230 static bool getEnabled(ControlState nState, AquaSalFrame* mpFrame)
233 // there are non key windows which are children of key windows, e.g. autofilter configuration dialog or sidebar dropdown dialogs.
234 // To handle these windows correctly, parent frame's key window state is considered here additionally.
236 const bool bDrawActive = mpFrame == nullptr || [mpFrame->getNSWindow() isKeyWindow]
237 || mpFrame->mpParent == nullptr || [mpFrame->mpParent->getNSWindow() isKeyWindow];
238 if (!(nState & ControlState::ENABLED) || !bDrawActive)
240 return false;
242 return true;
245 bool AquaSalGraphics::drawNativeControl(ControlType nType,
246 ControlPart nPart,
247 const tools::Rectangle &rControlRegion,
248 ControlState nState,
249 const ImplControlValue &aValue,
250 const OUString &,
251 const Color&)
253 return mpBackend->drawNativeControl(nType, nPart, rControlRegion, nState, aValue);
256 static void paintCell(NSCell* pBtn, const NSRect& bounds, bool bShowsFirstResponder, CGContextRef context, NSView* pView)
258 //translate and scale because up side down otherwise
259 CGContextSaveGState(context);
260 CGContextTranslateCTM(context, bounds.origin.x, bounds.origin.y + bounds.size.height);
261 CGContextScaleCTM(context, 1, -1);
263 NSGraphicsContext* savedContext = [NSGraphicsContext currentContext];
264 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithCGContext:context flipped:NO]];
266 NSRect rect = { NSZeroPoint, bounds.size };
268 if ([pBtn isKindOfClass: [NSSliderCell class]])
270 // NSSliderCell doesn't seem to work with drawWithFrame(?), so draw the elements directly
271 [static_cast<NSSliderCell*>(pBtn)
272 drawBarInside: [static_cast<NSSliderCell*>(pBtn) barRectFlipped: NO] flipped: NO];
273 rect = [static_cast<NSSliderCell*>(pBtn) knobRectFlipped: NO];
274 [static_cast<NSSliderCell*>(pBtn) drawKnob: rect];
276 else
277 [pBtn drawWithFrame: rect inView: pView];
279 // setShowsFirstResponder apparently causes a hang when set on NSComboBoxCell
280 const bool bIsComboBox = [pBtn isMemberOfClass: [NSComboBoxCell class]];
281 if (!bIsComboBox)
282 [pBtn setShowsFirstResponder: bShowsFirstResponder];
284 if (bShowsFirstResponder)
286 NSSetFocusRingStyle(NSFocusRingOnly);
288 CGContextBeginTransparencyLayerWithRect(context, rect, nullptr);
289 if ([pBtn isMemberOfClass: [NSTextFieldCell class]])
291 // I wonder why NSTextFieldCell doesn't work for me in the default else branch.
292 // NSComboBoxCell works, and that derives from NSTextFieldCell, on the other
293 // hand setShowsFirstResponder causes a hangs when set on NSComboBoxCell
294 NSRect out = [pBtn focusRingMaskBoundsForFrame: rect inView: pView];
295 CGContextFillRect(context, out);
297 else if ([pBtn isKindOfClass: [NSSliderCell class]])
299 // Not getting anything useful for a NSSliderCell, so use the knob
300 [static_cast<NSSliderCell*>(pBtn) drawKnob: rect];
302 else
303 [pBtn drawFocusRingMaskWithFrame:rect inView: pView];
305 CGContextEndTransparencyLayer(context);
308 [NSGraphicsContext setCurrentContext:savedContext];
309 CGContextRestoreGState(context);
312 static void paintFocusRect(double radius, const NSRect& rect, CGContextRef context)
314 NSRect bounds = rect;
316 CGPathRef path = CGPathCreateWithRoundedRect(bounds, radius, radius, nullptr);
317 CGContextSetStrokeColorWithColor(context, [NSColor keyboardFocusIndicatorColor].CGColor);
318 CGContextSetLineWidth(context, FOCUS_RING_WIDTH);
319 CGContextBeginPath(context);
320 CGContextAddPath(context, path);
321 CGContextStrokePath(context);
322 CFRelease(path);
325 @interface FixedWidthTabViewItem : NSTabViewItem {
326 int m_nWidth;
328 - (NSSize)sizeOfLabel: (BOOL)computeMin;
329 - (void)setTabWidth: (int)nWidth;
330 @end
332 @implementation FixedWidthTabViewItem
333 - (NSSize)sizeOfLabel: (BOOL)computeMin
335 NSSize size = [super sizeOfLabel: computeMin];
336 size.width = m_nWidth;
337 return size;
339 - (void)setTabWidth: (int)nWidth
341 m_nWidth = nWidth;
343 @end
345 bool AquaGraphicsBackend::drawNativeControl(ControlType nType,
346 ControlPart nPart,
347 const tools::Rectangle &rControlRegion,
348 ControlState nState,
349 const ImplControlValue &aValue)
351 if (!mrShared.checkContext())
352 return false;
353 mrShared.maContextHolder.saveState();
354 bool bOK = performDrawNativeControl(nType, nPart, rControlRegion, nState, aValue,
355 mrShared.maContextHolder.get(), mrShared.mpFrame);
356 mrShared.maContextHolder.restoreState();
358 tools::Rectangle buttonRect = rControlRegion;
360 // in most cases invalidating the whole control region instead of just the unclipped part of it is sufficient (and probably
361 // faster). However for the window background we should not unnecessarily enlarge the really changed rectangle since the
362 // difference is usually quite high. Background is always drawn as a whole since we don't know anything about its possible
363 // contents (see issue i90291).
365 if (nType == ControlType::WindowBackground)
367 CGRect aRect = {{0, 0}, {0, 0}};
368 if (mrShared.mxClipPath)
369 aRect = CGPathGetBoundingBox(mrShared.mxClipPath);
370 if (aRect.size.width != 0 && aRect.size.height != 0)
371 buttonRect.Intersection(tools::Rectangle(Point(static_cast<tools::Long>(aRect.origin.x),
372 static_cast<tools::Long>(aRect.origin.y)),
373 Size(static_cast<tools::Long>(aRect.size.width),
374 static_cast<tools::Long>(aRect.size.height))));
376 mrShared.refreshRect(buttonRect.Left(), buttonRect.Top(), buttonRect.GetWidth(), buttonRect.GetHeight());
377 return bOK;
380 static void drawBox(CGContextRef context, const NSRect& rc, NSColor* pColor)
382 CGContextSaveGState(context);
383 CGContextTranslateCTM(context, rc.origin.x, rc.origin.y + rc.size.height);
384 CGContextScaleCTM(context, 1, -1);
386 NSGraphicsContext* graphicsContext = [NSGraphicsContext graphicsContextWithCGContext:context flipped:NO];
388 NSRect rect = { NSZeroPoint, NSMakeSize(rc.size.width, rc.size.height) };
389 NSBox* pBox = [[NSBox alloc] initWithFrame: rect];
391 [pBox setBoxType: NSBoxCustom];
392 [pBox setFillColor: pColor];
393 SAL_WNODEPRECATED_DECLARATIONS_PUSH // setBorderType first deprecated in macOS 10.15
394 [pBox setBorderType: NSNoBorder];
395 SAL_WNODEPRECATED_DECLARATIONS_POP
397 [pBox displayRectIgnoringOpacity: rect inContext: graphicsContext];
399 [pBox release];
401 CGContextRestoreGState(context);
404 // if I don't crystallize this bg then the InvertCursor using kCGBlendModeDifference doesn't
405 // work correctly and the cursor doesn't appear correctly
406 static void drawEditableBackground(CGContextRef context, const NSRect& rc)
408 CGContextSaveGState(context);
409 CGContextSetFillColorWithColor(context, [NSColor controlBackgroundColor].CGColor);
410 CGContextFillRect(context, rc);
411 CGContextRestoreGState(context);
414 // As seen in macOS 12.3.1. All a bit odd really.
415 const int RoundedMargin[4] = { 6, 4, 0, 3 };
417 bool AquaGraphicsBackendBase::performDrawNativeControl(ControlType nType,
418 ControlPart nPart,
419 const tools::Rectangle &rControlRegion,
420 ControlState nState,
421 const ImplControlValue &aValue,
422 CGContextRef context,
423 AquaSalFrame* mpFrame)
425 bool bOK = false;
426 AquaSalInstance* pInst = GetSalData()->mpInstance;
427 HIRect rc = ImplGetHIRectFromRectangle(rControlRegion);
428 switch (nType)
430 case ControlType::Toolbar:
432 drawBox(context, rc, NSColor.windowBackgroundColor);
433 bOK = true;
435 break;
436 case ControlType::WindowBackground:
438 drawBox(context, rc, NSColor.windowBackgroundColor);
439 bOK = true;
441 break;
442 case ControlType::Tooltip:
444 rc.size.width += 2;
445 rc.size.height += 2;
446 drawBox(context, rc, NSColor.controlBackgroundColor);
447 bOK = true;
449 break;
450 case ControlType::Menubar:
451 case ControlType::MenuPopup:
452 if (nPart == ControlPart::Entire || nPart == ControlPart::MenuItem || nPart == ControlPart::HasBackgroundTexture)
454 // FIXME: without this magical offset there is a 2 pixel black border on the right
456 rc.size.width += 2;
457 HIThemeMenuDrawInfo aMenuInfo;
458 aMenuInfo.version = 0;
459 aMenuInfo.menuType = kThemeMenuTypePullDown;
460 HIThemeMenuItemDrawInfo aMenuItemDrawInfo;
462 // grey theme when the item is selected is drawn here.
464 aMenuItemDrawInfo.itemType = kThemeMenuItemPlain;
465 if ((nPart == ControlPart::MenuItem) && (nState & ControlState::SELECTED))
467 // blue theme when the item is selected is drawn here.
469 aMenuItemDrawInfo.state = kThemeMenuSelected;
470 else
472 // normal color for non selected item
474 aMenuItemDrawInfo.state = kThemeMenuActive;
476 // repaints the background of the pull down menu
478 HIThemeDrawMenuBackground(&rc, &aMenuInfo, context, kHIThemeOrientationNormal);
480 // repaints the item either blue (selected) and/or grey (active only)
482 HIThemeDrawMenuItem(&rc, &rc, &aMenuItemDrawInfo, context, kHIThemeOrientationNormal, &rc);
483 bOK = true;
485 else if (nPart == ControlPart::MenuItemCheckMark || nPart == ControlPart::MenuItemRadioMark)
487 // checked, else it is not displayed (see vcl/source/window/menu.cxx)
489 if (nState & ControlState::PRESSED)
491 HIThemeTextInfo aTextInfo;
492 aTextInfo.version = 0;
493 aTextInfo.state = (nState & ControlState::ENABLED) ? kThemeStateInactive: kThemeStateActive;
494 aTextInfo.fontID = kThemeMenuItemMarkFont;
495 aTextInfo.horizontalFlushness = kHIThemeTextHorizontalFlushCenter;
496 aTextInfo.verticalFlushness = kHIThemeTextVerticalFlushTop;
497 aTextInfo.options = kHIThemeTextBoxOptionNone;
498 aTextInfo.truncationPosition = kHIThemeTextTruncationNone;
500 // aTextInfo.truncationMaxLines unused because of kHIThemeTextTruncationNone item highlighted
502 if (nState & ControlState::SELECTED) aTextInfo.state = kThemeStatePressed;
503 UniChar mark=(nPart == ControlPart::MenuItemCheckMark) ? kCheckUnicode: kBulletUnicode;
504 CFStringRef cfString = CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, &mark, 1, kCFAllocatorNull);
505 HIThemeDrawTextBox(cfString, &rc, &aTextInfo, context, kHIThemeOrientationNormal);
506 if (cfString)
507 CFRelease(cfString);
508 bOK = true;
511 break;
512 case ControlType::Pushbutton:
514 NSControlSize eSizeKind = NSControlSizeRegular;
515 NSBezelStyle eBezelStyle = NSBezelStyleRounded;
517 PushButtonValue const *pPBVal = aValue.getType() == ControlType::Pushbutton ?
518 static_cast<PushButtonValue const *>(&aValue) : nullptr;
520 SInt32 nPaintHeight = rc.size.height;
521 if (rc.size.height <= PUSH_BUTTON_NORMAL_HEIGHT)
523 eSizeKind = NSControlSizeMini;
524 GetThemeMetric(kThemeMetricSmallPushButtonHeight, &nPaintHeight);
526 else if ((pPBVal && pPBVal->mbSingleLine) || rc.size.height < PUSH_BUTTON_NORMAL_HEIGHT * 3 / 2)
528 GetThemeMetric(kThemeMetricPushButtonHeight, &nPaintHeight);
530 else
532 // A simple square bezel style that can scale to any size
533 eBezelStyle = NSBezelStyleSmallSquare;
536 // translate the origin for controls with fixed paint height so content ends up somewhere sensible
537 rc.origin.y += (rc.size.height - nPaintHeight + 1) / 2;
538 rc.size.height = nPaintHeight;
540 NSButtonCell* pBtn = pInst->mpButtonCell;
541 pBtn.allowsMixedState = YES;
543 [pBtn setTitle: @""];
544 [pBtn setButtonType: NSButtonTypeMomentaryPushIn];
545 [pBtn setBezelStyle: eBezelStyle];
546 [pBtn setState: ImplGetButtonValue(aValue.getTristateVal())];
547 [pBtn setEnabled: getEnabled(nState, mpFrame)];
548 [pBtn setFocusRingType: NSFocusRingTypeExterior];
549 [pBtn setHighlighted: (nState & ControlState::PRESSED) ? YES : NO];
550 [pBtn setControlSize: eSizeKind];
551 if (nState & ControlState::DEFAULT)
552 [pBtn setKeyEquivalent: @"\r"];
553 else
554 [pBtn setKeyEquivalent: @""];
556 if (eBezelStyle == NSBezelStyleRounded)
558 int nMargin = RoundedMargin[eSizeKind];
559 rc.origin.x -= nMargin;
560 rc.size.width += nMargin * 2;
562 rc.origin.x += FOCUS_RING_WIDTH / 2;
563 rc.size.width -= FOCUS_RING_WIDTH;
566 const bool bFocused(nState & ControlState::FOCUSED);
567 paintCell(pBtn, rc, bFocused, context, nullptr);
569 bOK = true;
571 break;
572 case ControlType::Radiobutton:
573 case ControlType::Checkbox:
575 rc.size.width -= 2 * FOCUS_RING_WIDTH;
576 rc.size.height = RADIO_BUTTON_SMALL_SIZE;
577 rc.origin.x += FOCUS_RING_WIDTH;
578 rc.origin.y += FOCUS_RING_WIDTH;
580 NSButtonCell* pBtn = nType == ControlType::Checkbox ? pInst->mpCheckCell : pInst->mpRadioCell;
581 pBtn.allowsMixedState = YES;
583 [pBtn setTitle: @""];
584 [pBtn setButtonType: nType == ControlType::Checkbox ? NSButtonTypeSwitch : NSButtonTypeRadio];
585 [pBtn setState: ImplGetButtonValue(aValue.getTristateVal())];
586 [pBtn setEnabled: getEnabled(nState, mpFrame)];
587 [pBtn setFocusRingType: NSFocusRingTypeExterior];
588 [pBtn setHighlighted: (nState & ControlState::PRESSED) ? YES : NO];
590 const bool bFocused(nState & ControlState::FOCUSED);
591 paintCell(pBtn, rc, bFocused, context, nullptr);
593 bOK = true;
595 break;
596 case ControlType::ListNode:
598 NSButtonCell* pBtn = pInst->mpListNodeCell;
599 pBtn.allowsMixedState = YES;
601 [pBtn setTitle: @""];
602 [pBtn setButtonType: NSButtonTypeOnOff];
603 [pBtn setBezelStyle: NSBezelStyleDisclosure];
604 [pBtn setState: ImplGetButtonValue(aValue.getTristateVal())];
605 [pBtn setEnabled: getEnabled(nState, mpFrame)];
606 [pBtn setFocusRingType: NSFocusRingTypeExterior];
608 const bool bFocused(nState & ControlState::FOCUSED);
609 paintCell(pBtn, rc, bFocused, context, nullptr);
611 bOK = true;
613 break;
614 case ControlType::Progress:
615 case ControlType::IntroProgress:
617 NSRect rect = { NSZeroPoint, NSMakeSize(rc.size.width, rc.size.height) };
618 NSProgressIndicator* pBox = [[NSProgressIndicator alloc] initWithFrame: rect];
619 [pBox setControlSize: (rc.size.height > MEDIUM_PROGRESS_INDICATOR_HEIGHT) ?
620 NSControlSizeRegular : NSControlSizeSmall];
621 [pBox setMinValue: 0];
622 [pBox setMaxValue: rc.size.width];
623 [pBox setDoubleValue: aValue.getNumericVal()];
624 pBox.usesThreadedAnimation = NO;
625 [pBox setIndeterminate: NO];
627 CGContextSaveGState(context);
628 CGContextTranslateCTM(context, rc.origin.x, rc.origin.y);
630 NSGraphicsContext* savedContext = [NSGraphicsContext currentContext];
631 NSGraphicsContext* graphicsContext = [NSGraphicsContext graphicsContextWithCGContext:context flipped:NO];
632 [NSGraphicsContext setCurrentContext: graphicsContext];
634 [pBox drawRect: rect];
636 [NSGraphicsContext setCurrentContext: savedContext];
638 CGContextRestoreGState(context);
640 [pBox release];
642 bOK = true;
644 break;
645 case ControlType::Slider:
647 const SliderValue *pSliderVal = static_cast<SliderValue const *>(&aValue);
648 if (nPart == ControlPart::TrackHorzArea || nPart == ControlPart::TrackVertArea)
650 NSRect rect = { NSZeroPoint, NSMakeSize(rc.size.width, rc.size.height) };
651 NSSlider* pBox = [[NSSlider alloc] initWithFrame: rect];
653 [pBox setEnabled: getEnabled(nState, mpFrame)];
654 [pBox setVertical: nPart == ControlPart::TrackVertArea];
655 [pBox setMinValue: pSliderVal->mnMin];
656 [pBox setMaxValue: pSliderVal->mnMax];
657 [pBox setIntegerValue: pSliderVal->mnCur];
658 [pBox setSliderType: NSSliderTypeLinear];
659 [pBox setFocusRingType: NSFocusRingTypeExterior];
661 const bool bFocused(nState & ControlState::FOCUSED);
662 paintCell(pBox.cell, rc, bFocused, context, mpFrame->getNSView());
664 [pBox release];
666 bOK = true;
669 break;
670 case ControlType::Scrollbar:
672 const ScrollbarValue *pScrollbarVal = (aValue.getType() == ControlType::Scrollbar)
673 ? static_cast<const ScrollbarValue *>(&aValue) : nullptr;
674 if (nPart == ControlPart::DrawBackgroundVert || nPart == ControlPart::DrawBackgroundHorz)
676 drawBox(context, rc, NSColor.controlBackgroundColor);
678 NSRect rect = { NSZeroPoint, NSMakeSize(rc.size.width, rc.size.height) };
679 NSScroller* pBar = [[NSScroller alloc] initWithFrame: rect];
681 double range = pScrollbarVal->mnMax - pScrollbarVal->mnVisibleSize - pScrollbarVal->mnMin;
682 double value = range ? (pScrollbarVal->mnCur - pScrollbarVal->mnMin) / range : 0;
684 double length = pScrollbarVal->mnMax - pScrollbarVal->mnMin;
685 double proportion = pScrollbarVal->mnVisibleSize / length;
687 [pBar setEnabled: getEnabled(nState, mpFrame)];
688 [pBar setScrollerStyle: NSScrollerStyleLegacy];
689 [pBar setFloatValue: value];
690 [pBar setKnobProportion: proportion];
691 bool bPressed = (pScrollbarVal->mnThumbState & ControlState::ENABLED) &&
692 (pScrollbarVal->mnThumbState & ControlState::PRESSED);
694 CGContextSaveGState(context);
695 CGContextTranslateCTM(context, rc.origin.x, rc.origin.y);
697 NSGraphicsContext* graphicsContext = [NSGraphicsContext graphicsContextWithCGContext:context flipped:NO];
699 NSGraphicsContext* savedContext = [NSGraphicsContext currentContext];
700 [NSGraphicsContext setCurrentContext: graphicsContext];
702 // For not-pressed first draw without the knob and then
703 // draw just the knob but with 50% opaque which looks sort of
704 // right
706 [pBar drawKnobSlotInRect: rect highlight: NO];
708 NSBitmapImageRep* pImageRep = [pBar bitmapImageRepForCachingDisplayInRect: rect];
710 NSGraphicsContext* imageContext = [NSGraphicsContext graphicsContextWithBitmapImageRep:pImageRep];
711 [NSGraphicsContext setCurrentContext: imageContext];
713 [pBar drawKnob];
715 [NSGraphicsContext setCurrentContext: graphicsContext];
717 NSImage* pImage = [[NSImage alloc] initWithSize: rect.size];
718 [pImage addRepresentation: pImageRep]; // takes ownership of pImageRep
720 [pImage drawInRect: rect fromRect: rect
721 operation: NSCompositingOperationSourceOver
722 fraction: bPressed ? 1.0 : 0.5];
724 [pImage release];
726 [NSGraphicsContext setCurrentContext:savedContext];
728 CGContextRestoreGState(context);
730 bOK = true;
732 [pBar release];
735 break;
736 case ControlType::TabPane:
738 NSTabView* pBox = [[NSTabView alloc] initWithFrame: rc];
740 SInt32 nOverlap;
741 GetThemeMetric(kThemeMetricTabFrameOverlap, &nOverlap);
743 // this calculation is probably more than a little dubious
744 rc.origin.x -= pBox.contentRect.origin.x - FOCUS_RING_WIDTH;
745 rc.size.width += rc.size.width - pBox.contentRect.size.width - 2 * FOCUS_RING_WIDTH;
746 double nTopBorder = pBox.contentRect.origin.y;
747 double nBottomBorder = rc.size.height - pBox.contentRect.size.height - nTopBorder;
748 double nExtraTop = (nTopBorder - nBottomBorder) / 2;
749 rc.origin.y -= (nTopBorder - nExtraTop + nOverlap);
750 rc.size.height += (nTopBorder - nExtraTop + nBottomBorder);
752 CGContextSaveGState(context);
753 CGContextTranslateCTM(context, rc.origin.x, rc.origin.y);
755 rc.origin.x = 0;
756 rc.origin.y = 0;
758 [pBox setBoundsOrigin: rc.origin];
759 [pBox setBoundsSize: rc.size];
761 // jam this in to force the tab contents area to be left undrawn, the ControlType::TabItem
762 // will be drawn in this space.
763 const TabPaneValue& rValue = static_cast<const TabPaneValue&>(aValue);
764 SInt32 nEndCapWidth;
765 GetThemeMetric(kThemeMetricLargeTabCapsWidth, &nEndCapWidth);
766 FixedWidthTabViewItem* pItem = [[[FixedWidthTabViewItem alloc] initWithIdentifier: @"tab"] autorelease];
767 [pItem setTabWidth: rValue.m_aTabHeaderRect.GetWidth() - 2 * nEndCapWidth];
768 [pBox addTabViewItem: pItem];
770 NSGraphicsContext* graphicsContext = [NSGraphicsContext graphicsContextWithCGContext:context flipped:NO];
772 NSGraphicsContext* savedContext = [NSGraphicsContext currentContext];
773 [NSGraphicsContext setCurrentContext: graphicsContext];
775 [pBox drawRect: rc];
777 [NSGraphicsContext setCurrentContext: savedContext];
779 [pBox release];
781 CGContextRestoreGState(context);
783 bOK = true;
785 break;
786 case ControlType::TabItem:
788 // first, last or middle tab
790 TabitemValue const * pTabValue = static_cast<TabitemValue const *>(&aValue);
791 TabitemFlags nAlignment = pTabValue->mnAlignment;
793 // TabitemFlags::LeftAligned (and TabitemFlags::RightAligned) for the leftmost (or rightmost) tab
794 // when there are several lines of tabs because there is only one first tab and one
795 // last tab and TabitemFlags::FirstInGroup (and TabitemFlags::LastInGroup) because when the
796 // line width is different from window width, there may not be TabitemFlags::RightAligned
797 int nPaintIndex = 1;
798 bool bSolo = false;
799 if (((nAlignment & TabitemFlags::LeftAligned) && (nAlignment & TabitemFlags::RightAligned))
800 || ((nAlignment & TabitemFlags::FirstInGroup) && (nAlignment & TabitemFlags::LastInGroup)))
802 nPaintIndex = 0;
803 bSolo = true;
805 else if ((nAlignment & TabitemFlags::LeftAligned) || (nAlignment & TabitemFlags::FirstInGroup))
806 nPaintIndex = !AllSettings::GetLayoutRTL() ? 0 : 2;
807 else if ((nAlignment & TabitemFlags::RightAligned) || (nAlignment & TabitemFlags::LastInGroup))
808 nPaintIndex = !AllSettings::GetLayoutRTL() ? 2 : 0;
810 int nCells = !bSolo ? 3 : 1;
811 NSRect ctrlrect = { NSZeroPoint, NSMakeSize(rc.size.width * nCells + FOCUS_RING_WIDTH, rc.size.height) };
812 NSSegmentedControl* pCtrl = [[NSSegmentedControl alloc] initWithFrame: ctrlrect];
813 [pCtrl setSegmentCount: nCells];
814 if (bSolo)
815 [pCtrl setWidth: rc.size.width + FOCUS_RING_WIDTH forSegment: 0];
816 else
818 [pCtrl setWidth: rc.size.width + FOCUS_RING_WIDTH/2 forSegment: 0];
819 [pCtrl setWidth: rc.size.width forSegment: 1];
820 [pCtrl setWidth: rc.size.width + FOCUS_RING_WIDTH/2 forSegment: 2];
822 [pCtrl setSelected: (nState & ControlState::SELECTED) ? YES : NO forSegment: nPaintIndex];
823 [pCtrl setFocusRingType: NSFocusRingTypeExterior];
825 NSGraphicsContext* savedContext = [NSGraphicsContext currentContext];
826 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithCGContext:context flipped:NO]];
828 NSRect rect = { NSZeroPoint, NSMakeSize(rc.size.width, rc.size.height) };
829 NSRect tabrect = { NSMakePoint(rc.size.width * nPaintIndex + FOCUS_RING_WIDTH / 2, 0),
830 NSMakeSize(rc.size.width, rc.size.height) };
831 NSBitmapImageRep* pImageRep = [pCtrl bitmapImageRepForCachingDisplayInRect: tabrect];
832 [pCtrl cacheDisplayInRect: tabrect toBitmapImageRep: pImageRep];
834 NSImage* pImage = [[NSImage alloc] initWithSize: rect.size];
835 [pImage addRepresentation: pImageRep]; // takes ownership of pImageRep
837 [pImage drawInRect: rc fromRect: rect
838 operation: NSCompositingOperationSourceOver
839 fraction: 1.0];
841 [pImage release];
843 [NSGraphicsContext setCurrentContext:savedContext];
845 [pCtrl release];
847 if (nState & ControlState::FOCUSED)
849 if (!bSolo)
851 if (nPaintIndex == 0)
853 rc.origin.x += FOCUS_RING_WIDTH / 2;
854 rc.size.width -= FOCUS_RING_WIDTH / 2;
856 else if (nPaintIndex == 2)
858 rc.size.width -= FOCUS_RING_WIDTH / 2;
859 rc.size.width -= FOCUS_RING_WIDTH / 2;
863 paintFocusRect(4.0, rc, context);
865 bOK=true;
867 break;
868 case ControlType::Editbox:
869 case ControlType::MultilineEditbox:
871 rc.size.width += 2 * EDITBOX_INSET_MARGIN;
872 if (nType == ControlType::Editbox)
873 rc.size.height = EDITBOX_HEIGHT;
874 else
875 rc.size.height += 2 * (EDITBOX_BORDER_WIDTH + EDITBOX_INSET_MARGIN);
876 rc.origin.x -= EDITBOX_INSET_MARGIN;
877 rc.origin.y -= EDITBOX_INSET_MARGIN;
879 NSTextFieldCell* pBtn = pInst->mpTextFieldCell;
881 [pBtn setEnabled: getEnabled(nState, mpFrame)];
882 [pBtn setBezeled: YES];
883 [pBtn setEditable: YES];
884 [pBtn setFocusRingType: NSFocusRingTypeExterior];
886 drawEditableBackground(context, rc);
887 const bool bFocused(nState & ControlState::FOCUSED);
888 paintCell(pBtn, rc, bFocused, context, mpFrame->getNSView());
890 bOK = true;
892 break;
893 case ControlType::Combobox:
894 if (nPart == ControlPart::HasBackgroundTexture || nPart == ControlPart::Entire)
896 rc.origin.y += (rc.size.height - COMBOBOX_HEIGHT + 1) / 2;
897 rc.size.height = COMBOBOX_HEIGHT;
899 NSComboBoxCell* pBtn = pInst->mpComboBoxCell;
901 [pBtn setEnabled: getEnabled(nState, mpFrame)];
902 [pBtn setEditable: YES];
903 [pBtn setState: ImplGetButtonValue(aValue.getTristateVal())];
904 [pBtn setFocusRingType: NSFocusRingTypeExterior];
907 rc.origin.x += 2;
908 rc.size.width -= 1;
911 drawEditableBackground(context, rc);
912 const bool bFocused(nState & ControlState::FOCUSED);
913 paintCell(pBtn, rc, bFocused, context, mpFrame->getNSView());
915 bOK = true;
917 break;
918 case ControlType::Listbox:
920 switch (nPart)
922 case ControlPart::Entire:
923 case ControlPart::ButtonDown:
925 rc.origin.y += (rc.size.height - LISTBOX_HEIGHT + 1) / 2;
926 rc.size.height = LISTBOX_HEIGHT;
928 NSPopUpButtonCell* pBtn = pInst->mpPopUpButtonCell;
930 [pBtn setTitle: @""];
931 [pBtn setEnabled: getEnabled(nState, mpFrame)];
932 [pBtn setFocusRingType: NSFocusRingTypeExterior];
933 [pBtn setHighlighted: (nState & ControlState::PRESSED) ? YES : NO];
934 if (nState & ControlState::DEFAULT)
935 [pBtn setKeyEquivalent: @"\r"];
936 else
937 [pBtn setKeyEquivalent: @""];
940 rc.size.width += 1;
943 const bool bFocused(nState & ControlState::FOCUSED);
944 paintCell(pBtn, rc, bFocused, context, nullptr);
946 bOK = true;
947 break;
949 case ControlPart::ListboxWindow:
951 NSRect rect = { NSZeroPoint, NSMakeSize(rc.size.width, rc.size.height) };
952 NSScrollView* pBox = [[NSScrollView alloc] initWithFrame: rect];
953 [pBox setBorderType: NSLineBorder];
955 CGContextSaveGState(context);
956 CGContextTranslateCTM(context, rc.origin.x, rc.origin.y);
958 NSGraphicsContext* savedContext = [NSGraphicsContext currentContext];
959 NSGraphicsContext* graphicsContext = [NSGraphicsContext graphicsContextWithCGContext:context flipped:NO];
960 [NSGraphicsContext setCurrentContext: graphicsContext];
962 [pBox drawRect: rect];
964 [NSGraphicsContext setCurrentContext: savedContext];
966 CGContextRestoreGState(context);
968 [pBox release];
970 bOK = true;
971 break;
973 default:
974 break;
976 break;
977 case ControlType::Spinbox:
978 if (nPart == ControlPart::Entire)
980 // text field
982 rc.size.width -= SPIN_BUTTON_WIDTH + 4 * FOCUS_RING_WIDTH;
983 rc.size.height = EDITBOX_HEIGHT;
984 rc.origin.x += FOCUS_RING_WIDTH;
985 rc.origin.y += FOCUS_RING_WIDTH;
987 NSTextFieldCell* pEdit = pInst->mpTextFieldCell;
989 [pEdit setEnabled: YES];
990 [pEdit setBezeled: YES];
991 [pEdit setEditable: YES];
992 [pEdit setFocusRingType: NSFocusRingTypeExterior];
994 drawEditableBackground(context, rc);
995 const bool bFocused(nState & ControlState::FOCUSED);
996 paintCell(pEdit, rc, bFocused, context, mpFrame->getNSView());
998 // buttons
1000 const SpinbuttonValue *pSpinButtonVal = (aValue.getType() == ControlType::SpinButtons)
1001 ? static_cast <const SpinbuttonValue *>(&aValue) : nullptr;
1002 if (pSpinButtonVal)
1004 ControlState nUpperState = pSpinButtonVal->mnUpperState;
1005 ControlState nLowerState = pSpinButtonVal->mnLowerState;
1007 rc.origin.x += rc.size.width + FOCUS_RING_WIDTH + 1;
1008 rc.origin.y -= 1;
1009 rc.size.width = SPIN_BUTTON_WIDTH;
1010 rc.size.height = SPIN_LOWER_BUTTON_HEIGHT + SPIN_LOWER_BUTTON_HEIGHT;
1012 NSStepperCell* pBtn = pInst->mpStepperCell;
1014 [pBtn setTitle: @""];
1015 [pBtn setState: ImplGetButtonValue(aValue.getTristateVal())];
1016 [pBtn setEnabled: (nUpperState & ControlState::ENABLED || nLowerState & ControlState::ENABLED) ?
1017 YES : NO];
1018 [pBtn setFocusRingType: NSFocusRingTypeExterior];
1019 [pBtn setHighlighted: (nState & ControlState::PRESSED) ? YES : NO];
1021 const bool bSpinFocused(nUpperState & ControlState::FOCUSED || nLowerState & ControlState::FOCUSED);
1022 paintCell(pBtn, rc, bSpinFocused, context, nullptr);
1024 bOK = true;
1026 break;
1027 case ControlType::Frame:
1029 DrawFrameFlags nStyle = static_cast<DrawFrameFlags>(aValue.getNumericVal());
1030 if (nPart == ControlPart::Border)
1032 if (!(nStyle & DrawFrameFlags::Menu) && !(nStyle & DrawFrameFlags::WindowBorder))
1035 // strange effects start to happen when HIThemeDrawFrame meets the border of the window.
1036 // These can be avoided by clipping to the boundary of the frame (see issue 84756)
1038 if (rc.origin.y + rc.size.height >= mpFrame->maGeometry.height() - 3)
1040 CGMutablePathRef rPath = CGPathCreateMutable();
1041 CGPathAddRect(rPath, nullptr,
1042 CGRectMake(0, 0, mpFrame->maGeometry.width() - 1, mpFrame->maGeometry.height() - 1));
1043 CGContextBeginPath(context);
1044 CGContextAddPath(context, rPath);
1045 CGContextClip(context);
1046 CGPathRelease(rPath);
1048 HIThemeFrameDrawInfo aTextDrawInfo;
1049 aTextDrawInfo.version = 0;
1050 aTextDrawInfo.kind = kHIThemeFrameListBox;
1051 aTextDrawInfo.state = kThemeStateActive;
1052 aTextDrawInfo.isFocused = false;
1053 HIThemeDrawFrame(&rc, &aTextDrawInfo, context, kHIThemeOrientationNormal);
1054 bOK = true;
1058 break;
1059 case ControlType::ListNet:
1061 // do nothing as there isn't net for listviews on macOS
1063 bOK = true;
1064 break;
1065 default:
1066 break;
1069 return bOK;
1072 bool AquaSalGraphics::getNativeControlRegion(ControlType nType,
1073 ControlPart nPart,
1074 const tools::Rectangle &rControlRegion,
1075 ControlState,
1076 const ImplControlValue &aValue,
1077 const OUString &,
1078 tools::Rectangle &rNativeBoundingRegion,
1079 tools::Rectangle &rNativeContentRegion)
1081 bool toReturn = false;
1082 tools::Rectangle aCtrlBoundRect(rControlRegion);
1083 short x = aCtrlBoundRect.Left();
1084 short y = aCtrlBoundRect.Top();
1085 short w, h;
1086 switch (nType)
1088 case ControlType::Pushbutton:
1089 case ControlType::Radiobutton:
1090 case ControlType::Checkbox:
1092 if (nType == ControlType::Pushbutton)
1094 w = aCtrlBoundRect.GetWidth();
1095 h = aCtrlBoundRect.GetHeight();
1097 else
1099 w = RADIO_BUTTON_SMALL_SIZE + 2 * FOCUS_RING_WIDTH + RADIO_BUTTON_TEXT_SEPARATOR;
1100 h = RADIO_BUTTON_SMALL_SIZE + 2 * FOCUS_RING_WIDTH;
1102 rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h));
1103 rNativeBoundingRegion = tools::Rectangle(Point(x, y), Size(w, h));
1104 toReturn = true;
1106 break;
1107 case ControlType::Progress:
1109 tools::Rectangle aRect(aCtrlBoundRect);
1110 if (aRect.GetHeight() < LARGE_PROGRESS_INDICATOR_HEIGHT)
1111 aRect.SetBottom(aRect.Top() + MEDIUM_PROGRESS_INDICATOR_HEIGHT - 1);
1112 else
1113 aRect.SetBottom(aRect.Top() + LARGE_PROGRESS_INDICATOR_HEIGHT - 1);
1114 rNativeBoundingRegion = aRect;
1115 rNativeContentRegion = aRect;
1116 toReturn = true;
1118 break;
1119 case ControlType::IntroProgress:
1121 tools::Rectangle aRect(aCtrlBoundRect);
1122 aRect.SetBottom(aRect.Top() + MEDIUM_PROGRESS_INDICATOR_HEIGHT - 1);
1123 rNativeBoundingRegion = aRect;
1124 rNativeContentRegion = aRect;
1125 toReturn = true;
1127 break;
1128 case ControlType::Slider:
1129 if (nPart == ControlPart::ThumbHorz)
1131 w = SLIDER_WIDTH;
1132 h = aCtrlBoundRect.GetHeight();
1133 rNativeBoundingRegion = rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h));
1134 toReturn = true;
1136 else if (nPart == ControlPart::ThumbVert)
1138 w = aCtrlBoundRect.GetWidth();
1139 h = SLIDER_HEIGHT;
1140 rNativeBoundingRegion = rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h));
1141 toReturn = true;
1143 break;
1144 case ControlType::Scrollbar:
1146 tools::Rectangle aRect;
1147 if (AquaGetScrollRect(nPart, aCtrlBoundRect, aRect))
1149 toReturn = true;
1150 rNativeBoundingRegion = aRect;
1151 rNativeContentRegion = aRect;
1154 break;
1155 case ControlType::TabItem:
1157 w = aCtrlBoundRect.GetWidth() + 2 * TAB_TEXT_MARGIN;
1158 h = TAB_HEIGHT + 2;
1159 rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h));
1160 rNativeBoundingRegion = tools::Rectangle(Point(x, y), Size(w, h));
1161 toReturn = true;
1163 break;
1164 case ControlType::Editbox:
1166 const tools::Long nBorderThickness = FOCUS_RING_WIDTH + EDITBOX_BORDER_WIDTH + EDITBOX_INSET_MARGIN;
1167 // tdf#144241 don't return a negative width, expand the region to the min osx width
1168 w = std::max(nBorderThickness * 2, aCtrlBoundRect.GetWidth());
1169 h = EDITBOX_HEIGHT + 2 * FOCUS_RING_WIDTH;
1170 rNativeBoundingRegion = tools::Rectangle(Point(x, y), Size(w, h));
1171 w -= 2 * nBorderThickness;
1172 h -= 2 * nBorderThickness;
1173 x += nBorderThickness;
1174 y += nBorderThickness;
1175 rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h));
1176 toReturn = true;
1178 break;
1179 case ControlType::Combobox:
1180 if (nPart == ControlPart::Entire)
1182 w = aCtrlBoundRect.GetWidth();
1183 h = COMBOBOX_HEIGHT + 2 * FOCUS_RING_WIDTH;
1184 rNativeBoundingRegion = tools::Rectangle(Point(x, y), Size(w, h));
1185 rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h));
1186 toReturn = true;
1188 else if (nPart == ControlPart::ButtonDown)
1190 w = COMBOBOX_BUTTON_WIDTH + FOCUS_RING_WIDTH;
1191 h = COMBOBOX_HEIGHT + 2 * FOCUS_RING_WIDTH;
1192 x += aCtrlBoundRect.GetWidth() - w;
1193 rNativeBoundingRegion = tools::Rectangle(Point(x, y), Size(w, h));
1194 rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h));
1195 toReturn = true;
1197 else if (nPart == ControlPart::SubEdit)
1199 w = aCtrlBoundRect.GetWidth() - 2 * FOCUS_RING_WIDTH - COMBOBOX_BUTTON_WIDTH - COMBOBOX_BORDER_WIDTH
1200 - 2 * COMBOBOX_TEXT_MARGIN;
1201 h = COMBOBOX_HEIGHT - 2 * COMBOBOX_BORDER_WIDTH;
1202 x += FOCUS_RING_WIDTH + COMBOBOX_BORDER_WIDTH + COMBOBOX_TEXT_MARGIN;
1203 y += FOCUS_RING_WIDTH + COMBOBOX_BORDER_WIDTH;
1204 rNativeBoundingRegion = tools::Rectangle(Point(x, y), Size(w, h));
1205 rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h));
1206 toReturn = true;
1208 break;
1209 case ControlType::Listbox:
1210 if (nPart == ControlPart::Entire)
1212 w = aCtrlBoundRect.GetWidth();
1213 h = LISTBOX_HEIGHT + 2 * FOCUS_RING_WIDTH;
1214 rNativeBoundingRegion = tools::Rectangle(Point(x, y), Size(w, h));
1215 rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h));
1216 toReturn = true;
1218 else if (nPart == ControlPart::ButtonDown)
1220 w = LISTBOX_BUTTON_WIDTH + FOCUS_RING_WIDTH;
1221 h = LISTBOX_HEIGHT + 2 * FOCUS_RING_WIDTH;
1222 x += aCtrlBoundRect.GetWidth() - w;
1223 rNativeBoundingRegion = tools::Rectangle(Point(x, y), Size(w, h));
1224 rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h));
1225 toReturn = true;
1227 else if (nPart == ControlPart::SubEdit)
1229 w = aCtrlBoundRect.GetWidth() - 2 * FOCUS_RING_WIDTH - LISTBOX_BUTTON_WIDTH - LISTBOX_BORDER_WIDTH
1230 - 2 * LISTBOX_TEXT_MARGIN;
1231 h = LISTBOX_HEIGHT - 2 * LISTBOX_BORDER_WIDTH;
1232 x += FOCUS_RING_WIDTH + LISTBOX_BORDER_WIDTH + LISTBOX_TEXT_MARGIN;
1233 y += FOCUS_RING_WIDTH + LISTBOX_BORDER_WIDTH;
1234 rNativeBoundingRegion = tools::Rectangle(Point(x, y), Size(w, h));
1235 rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h));
1236 toReturn = true;
1238 else if (nPart == ControlPart::ListboxWindow)
1240 w = aCtrlBoundRect.GetWidth() - 2;
1241 h = aCtrlBoundRect.GetHeight() - 2;
1242 x += 1;
1243 y += 1;
1244 rNativeBoundingRegion = aCtrlBoundRect;
1245 rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h));
1246 toReturn = true;
1248 break;
1249 case ControlType::Spinbox:
1250 if (nPart == ControlPart::Entire)
1252 w = aCtrlBoundRect.GetWidth();
1253 h = EDITBOX_HEIGHT + 2 * FOCUS_RING_WIDTH;
1254 x += SPINBOX_OFFSET;
1255 rNativeBoundingRegion = tools::Rectangle(Point(x, y), Size(w, h));
1256 rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h));
1257 toReturn = true;
1259 else if (nPart == ControlPart::SubEdit)
1261 w = aCtrlBoundRect.GetWidth() - 4 * FOCUS_RING_WIDTH - SPIN_BUTTON_WIDTH - 2 * EDITBOX_BORDER_WIDTH
1262 - 2 * EDITBOX_INSET_MARGIN;
1263 h = EDITBOX_HEIGHT - 2 * (EDITBOX_BORDER_WIDTH + EDITBOX_INSET_MARGIN);
1264 x += FOCUS_RING_WIDTH + EDITBOX_BORDER_WIDTH + EDITBOX_INSET_MARGIN + SPINBOX_OFFSET;
1265 y += FOCUS_RING_WIDTH + EDITBOX_BORDER_WIDTH + EDITBOX_INSET_MARGIN;
1266 rNativeBoundingRegion = tools::Rectangle(Point(x, y), Size(w, h));
1267 rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h));
1268 toReturn = true;
1270 else if (nPart == ControlPart::ButtonUp)
1272 w = SPIN_BUTTON_WIDTH + 2 * FOCUS_RING_WIDTH;
1273 h = SPIN_UPPER_BUTTON_HEIGHT + FOCUS_RING_WIDTH;
1274 x += aCtrlBoundRect.GetWidth() - SPIN_BUTTON_WIDTH - 2 * FOCUS_RING_WIDTH + SPINBOX_OFFSET;
1275 rNativeBoundingRegion = tools::Rectangle(Point(x, y), Size(w, h));
1276 rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h));
1277 toReturn = true;
1279 else if (nPart == ControlPart::ButtonDown)
1281 w = SPIN_BUTTON_WIDTH + 2 * FOCUS_RING_WIDTH;
1282 h = SPIN_LOWER_BUTTON_HEIGHT + FOCUS_RING_WIDTH;
1283 x += aCtrlBoundRect.GetWidth() - SPIN_BUTTON_WIDTH - 2 * FOCUS_RING_WIDTH + SPINBOX_OFFSET;
1284 y += FOCUS_RING_WIDTH + SPIN_UPPER_BUTTON_HEIGHT;
1285 rNativeBoundingRegion = tools::Rectangle(Point(x, y), Size(w, h));
1286 rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h));
1287 toReturn = true;
1289 break;
1290 case ControlType::Frame:
1292 DrawFrameStyle nStyle = static_cast<DrawFrameStyle>(aValue.getNumericVal() & 0x000f);
1293 DrawFrameFlags nFlags = static_cast<DrawFrameFlags>(aValue.getNumericVal() & 0xfff0);
1294 if (nPart == ControlPart::Border
1295 && !(nFlags & (DrawFrameFlags::Menu | DrawFrameFlags::WindowBorder | DrawFrameFlags::BorderWindowBorder)))
1297 tools::Rectangle aRect(aCtrlBoundRect);
1298 if (nStyle == DrawFrameStyle::DoubleIn)
1300 aRect.AdjustLeft(1);
1301 aRect.AdjustTop(1);
1302 // rRect.Right() -= 1;
1303 // rRect.Bottom() -= 1;
1305 else
1307 aRect.AdjustLeft(1);
1308 aRect.AdjustTop(1);
1309 aRect.AdjustRight(-1);
1310 aRect.AdjustBottom(-1);
1312 rNativeContentRegion = aRect;
1313 rNativeBoundingRegion = aRect;
1314 toReturn = true;
1317 break;
1318 case ControlType::Menubar:
1319 case ControlType::MenuPopup:
1320 if (nPart == ControlPart::MenuItemCheckMark || nPart == ControlPart::MenuItemRadioMark)
1322 w=10;
1323 h=10;
1324 rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h));
1325 rNativeBoundingRegion = tools::Rectangle(Point(x, y), Size(w, h));
1326 toReturn = true;
1328 break;
1329 default:
1330 break;
1332 return toReturn;
1335 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */