Update git submodules
[LibreOffice.git] / vcl / osx / salnativewidgets.cxx
blob8a7e81fd5d86d0b691040deb5977c9124d105348
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::LevelBar:
198 case ControlType::Progress:
199 case ControlType::IntroProgress:
200 if (nPart == ControlPart::Entire)
201 return true;
202 break;
203 case ControlType::Frame:
204 if (nPart == ControlPart::Border)
205 return true;
206 break;
207 case ControlType::ListNet:
208 if (nPart == ControlPart::Entire)
209 return true;
210 break;
211 default:
212 break;
214 return false;
217 bool AquaSalGraphics::hitTestNativeControl(ControlType nType, ControlPart nPart, const tools::Rectangle &rControlRegion,
218 const Point &rPos, bool& rIsInside)
220 if (nType == ControlType::Scrollbar)
222 tools::Rectangle aRect;
223 bool bValid = AquaGetScrollRect(/* TODO: int nScreen, */
224 nPart, rControlRegion, aRect);
225 rIsInside = bValid && aRect.Contains(rPos);
226 return bValid;
228 return false;
231 static bool getEnabled(ControlState nState, AquaSalFrame* mpFrame)
234 // there are non key windows which are children of key windows, e.g. autofilter configuration dialog or sidebar dropdown dialogs.
235 // To handle these windows correctly, parent frame's key window state is considered here additionally.
237 const bool bDrawActive = mpFrame == nullptr || [mpFrame->getNSWindow() isKeyWindow]
238 || mpFrame->mpParent == nullptr || [mpFrame->mpParent->getNSWindow() isKeyWindow];
239 if (!(nState & ControlState::ENABLED) || !bDrawActive)
241 return false;
243 return true;
246 bool AquaSalGraphics::drawNativeControl(ControlType nType,
247 ControlPart nPart,
248 const tools::Rectangle &rControlRegion,
249 ControlState nState,
250 const ImplControlValue &aValue,
251 const OUString &,
252 const Color&)
254 return mpBackend->drawNativeControl(nType, nPart, rControlRegion, nState, aValue);
257 static void paintCell(NSCell* pBtn, const NSRect& bounds, bool bShowsFirstResponder, CGContextRef context, NSView* pView)
259 //translate and scale because up side down otherwise
260 CGContextSaveGState(context);
261 CGContextTranslateCTM(context, bounds.origin.x, bounds.origin.y + bounds.size.height);
262 CGContextScaleCTM(context, 1, -1);
264 NSGraphicsContext* savedContext = [NSGraphicsContext currentContext];
265 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithCGContext:context flipped:NO]];
267 NSRect rect = { NSZeroPoint, bounds.size };
269 if ([pBtn isKindOfClass: [NSSliderCell class]])
271 // NSSliderCell doesn't seem to work with drawWithFrame(?), so draw the elements directly
272 [static_cast<NSSliderCell*>(pBtn)
273 drawBarInside: [static_cast<NSSliderCell*>(pBtn) barRectFlipped: NO] flipped: NO];
274 rect = [static_cast<NSSliderCell*>(pBtn) knobRectFlipped: NO];
275 [static_cast<NSSliderCell*>(pBtn) drawKnob: rect];
277 else
278 [pBtn drawWithFrame: rect inView: pView];
280 // setShowsFirstResponder apparently causes a hang when set on NSComboBoxCell
281 const bool bIsComboBox = [pBtn isMemberOfClass: [NSComboBoxCell class]];
282 if (!bIsComboBox)
283 [pBtn setShowsFirstResponder: bShowsFirstResponder];
285 if (bShowsFirstResponder)
287 NSSetFocusRingStyle(NSFocusRingOnly);
289 CGContextBeginTransparencyLayerWithRect(context, rect, nullptr);
290 if ([pBtn isMemberOfClass: [NSTextFieldCell class]])
292 // I wonder why NSTextFieldCell doesn't work for me in the default else branch.
293 // NSComboBoxCell works, and that derives from NSTextFieldCell, on the other
294 // hand setShowsFirstResponder causes a hangs when set on NSComboBoxCell
295 NSRect out = [pBtn focusRingMaskBoundsForFrame: rect inView: pView];
296 CGContextFillRect(context, out);
298 else if ([pBtn isKindOfClass: [NSSliderCell class]])
300 // Not getting anything useful for a NSSliderCell, so use the knob
301 [static_cast<NSSliderCell*>(pBtn) drawKnob: rect];
303 else
304 [pBtn drawFocusRingMaskWithFrame:rect inView: pView];
306 CGContextEndTransparencyLayer(context);
309 [NSGraphicsContext setCurrentContext:savedContext];
310 CGContextRestoreGState(context);
313 static void paintFocusRect(double radius, const NSRect& rect, CGContextRef context)
315 NSRect bounds = rect;
317 CGPathRef path = CGPathCreateWithRoundedRect(bounds, radius, radius, nullptr);
318 CGContextSetStrokeColorWithColor(context, [NSColor keyboardFocusIndicatorColor].CGColor);
319 CGContextSetLineWidth(context, FOCUS_RING_WIDTH);
320 CGContextBeginPath(context);
321 CGContextAddPath(context, path);
322 CGContextStrokePath(context);
323 CFRelease(path);
326 @interface FixedWidthTabViewItem : NSTabViewItem {
327 int m_nWidth;
329 - (NSSize)sizeOfLabel: (BOOL)computeMin;
330 - (void)setTabWidth: (int)nWidth;
331 @end
333 @implementation FixedWidthTabViewItem
334 - (NSSize)sizeOfLabel: (BOOL)computeMin
336 NSSize size = [super sizeOfLabel: computeMin];
337 size.width = m_nWidth;
338 return size;
340 - (void)setTabWidth: (int)nWidth
342 m_nWidth = nWidth;
344 @end
346 bool AquaGraphicsBackend::drawNativeControl(ControlType nType,
347 ControlPart nPart,
348 const tools::Rectangle &rControlRegion,
349 ControlState nState,
350 const ImplControlValue &aValue)
352 if (!mrShared.checkContext())
353 return false;
354 mrShared.maContextHolder.saveState();
355 bool bOK = performDrawNativeControl(nType, nPart, rControlRegion, nState, aValue,
356 mrShared.maContextHolder.get(), mrShared.mpFrame);
357 mrShared.maContextHolder.restoreState();
359 tools::Rectangle buttonRect = rControlRegion;
361 // in most cases invalidating the whole control region instead of just the unclipped part of it is sufficient (and probably
362 // faster). However for the window background we should not unnecessarily enlarge the really changed rectangle since the
363 // difference is usually quite high. Background is always drawn as a whole since we don't know anything about its possible
364 // contents (see issue i90291).
366 if (nType == ControlType::WindowBackground)
368 CGRect aRect = {{0, 0}, {0, 0}};
369 if (mrShared.mxClipPath)
370 aRect = CGPathGetBoundingBox(mrShared.mxClipPath);
371 if (aRect.size.width != 0 && aRect.size.height != 0)
372 buttonRect.Intersection(tools::Rectangle(Point(static_cast<tools::Long>(aRect.origin.x),
373 static_cast<tools::Long>(aRect.origin.y)),
374 Size(static_cast<tools::Long>(aRect.size.width),
375 static_cast<tools::Long>(aRect.size.height))));
377 mrShared.refreshRect(buttonRect.Left(), buttonRect.Top(), buttonRect.GetWidth(), buttonRect.GetHeight());
378 return bOK;
381 static void drawBox(CGContextRef context, const NSRect& rc, NSColor* pColor)
383 CGContextSaveGState(context);
384 CGContextTranslateCTM(context, rc.origin.x, rc.origin.y + rc.size.height);
385 CGContextScaleCTM(context, 1, -1);
387 NSGraphicsContext* graphicsContext = [NSGraphicsContext graphicsContextWithCGContext:context flipped:NO];
389 NSRect rect = { NSZeroPoint, NSMakeSize(rc.size.width, rc.size.height) };
390 NSBox* pBox = [[NSBox alloc] initWithFrame: rect];
392 [pBox setBoxType: NSBoxCustom];
393 [pBox setFillColor: pColor];
394 SAL_WNODEPRECATED_DECLARATIONS_PUSH // setBorderType first deprecated in macOS 10.15
395 [pBox setBorderType: NSNoBorder];
396 SAL_WNODEPRECATED_DECLARATIONS_POP
398 [pBox displayRectIgnoringOpacity: rect inContext: graphicsContext];
400 [pBox release];
402 CGContextRestoreGState(context);
405 // if I don't crystallize this bg then the InvertCursor using kCGBlendModeDifference doesn't
406 // work correctly and the cursor doesn't appear correctly
407 static void drawEditableBackground(CGContextRef context, const NSRect& rc)
409 CGContextSaveGState(context);
410 CGContextSetFillColorWithColor(context, [NSColor controlBackgroundColor].CGColor);
411 CGContextFillRect(context, rc);
412 CGContextRestoreGState(context);
415 // As seen in macOS 12.3.1. All a bit odd really.
416 const int RoundedMargin[4] = { 6, 4, 0, 3 };
418 bool AquaGraphicsBackendBase::performDrawNativeControl(ControlType nType,
419 ControlPart nPart,
420 const tools::Rectangle &rControlRegion,
421 ControlState nState,
422 const ImplControlValue &aValue,
423 CGContextRef context,
424 AquaSalFrame* mpFrame)
426 bool bOK = false;
427 AquaSalInstance* pInst = GetSalData()->mpInstance;
428 HIRect rc = ImplGetHIRectFromRectangle(rControlRegion);
429 switch (nType)
431 case ControlType::Toolbar:
433 drawBox(context, rc, NSColor.windowBackgroundColor);
434 bOK = true;
436 break;
437 case ControlType::WindowBackground:
439 drawBox(context, rc, NSColor.windowBackgroundColor);
440 bOK = true;
442 break;
443 case ControlType::Tooltip:
445 rc.size.width += 2;
446 rc.size.height += 2;
447 drawBox(context, rc, NSColor.controlBackgroundColor);
448 bOK = true;
450 break;
451 case ControlType::Menubar:
452 case ControlType::MenuPopup:
453 if (nPart == ControlPart::Entire || nPart == ControlPart::MenuItem || nPart == ControlPart::HasBackgroundTexture)
455 // FIXME: without this magical offset there is a 2 pixel black border on the right
457 rc.size.width += 2;
458 HIThemeMenuDrawInfo aMenuInfo;
459 aMenuInfo.version = 0;
460 aMenuInfo.menuType = kThemeMenuTypePullDown;
461 HIThemeMenuItemDrawInfo aMenuItemDrawInfo;
463 // grey theme when the item is selected is drawn here.
465 aMenuItemDrawInfo.itemType = kThemeMenuItemPlain;
466 if ((nPart == ControlPart::MenuItem) && (nState & ControlState::SELECTED))
468 // blue theme when the item is selected is drawn here.
470 aMenuItemDrawInfo.state = kThemeMenuSelected;
471 else
473 // normal color for non selected item
475 aMenuItemDrawInfo.state = kThemeMenuActive;
477 // repaints the background of the pull down menu
479 HIThemeDrawMenuBackground(&rc, &aMenuInfo, context, kHIThemeOrientationNormal);
481 // repaints the item either blue (selected) and/or grey (active only)
483 HIThemeDrawMenuItem(&rc, &rc, &aMenuItemDrawInfo, context, kHIThemeOrientationNormal, &rc);
484 bOK = true;
486 else if (nPart == ControlPart::MenuItemCheckMark || nPart == ControlPart::MenuItemRadioMark)
488 // checked, else it is not displayed (see vcl/source/window/menu.cxx)
490 if (nState & ControlState::PRESSED)
492 HIThemeTextInfo aTextInfo;
493 aTextInfo.version = 0;
494 aTextInfo.state = (nState & ControlState::ENABLED) ? kThemeStateInactive: kThemeStateActive;
495 aTextInfo.fontID = kThemeMenuItemMarkFont;
496 aTextInfo.horizontalFlushness = kHIThemeTextHorizontalFlushCenter;
497 aTextInfo.verticalFlushness = kHIThemeTextVerticalFlushTop;
498 aTextInfo.options = kHIThemeTextBoxOptionNone;
499 aTextInfo.truncationPosition = kHIThemeTextTruncationNone;
501 // aTextInfo.truncationMaxLines unused because of kHIThemeTextTruncationNone item highlighted
503 if (nState & ControlState::SELECTED) aTextInfo.state = kThemeStatePressed;
504 UniChar mark=(nPart == ControlPart::MenuItemCheckMark) ? kCheckUnicode: kBulletUnicode;
505 CFStringRef cfString = CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, &mark, 1, kCFAllocatorNull);
506 HIThemeDrawTextBox(cfString, &rc, &aTextInfo, context, kHIThemeOrientationNormal);
507 if (cfString)
508 CFRelease(cfString);
509 bOK = true;
512 break;
513 case ControlType::Pushbutton:
515 NSControlSize eSizeKind = NSControlSizeRegular;
516 NSBezelStyle eBezelStyle = NSBezelStyleRounded;
518 PushButtonValue const *pPBVal = aValue.getType() == ControlType::Pushbutton ?
519 static_cast<PushButtonValue const *>(&aValue) : nullptr;
521 SInt32 nPaintHeight = rc.size.height;
522 if (rc.size.height <= PUSH_BUTTON_NORMAL_HEIGHT)
524 eSizeKind = NSControlSizeMini;
525 GetThemeMetric(kThemeMetricSmallPushButtonHeight, &nPaintHeight);
527 else if ((pPBVal && pPBVal->mbSingleLine) || rc.size.height < PUSH_BUTTON_NORMAL_HEIGHT * 3 / 2)
529 GetThemeMetric(kThemeMetricPushButtonHeight, &nPaintHeight);
531 else
533 // A simple square bezel style that can scale to any size
534 eBezelStyle = NSBezelStyleSmallSquare;
537 // translate the origin for controls with fixed paint height so content ends up somewhere sensible
538 rc.origin.y += (rc.size.height - nPaintHeight + 1) / 2;
539 rc.size.height = nPaintHeight;
541 NSButtonCell* pBtn = pInst->mpButtonCell;
542 pBtn.allowsMixedState = YES;
544 [pBtn setTitle: @""];
545 [pBtn setButtonType: NSButtonTypeMomentaryPushIn];
546 [pBtn setBezelStyle: eBezelStyle];
547 [pBtn setState: ImplGetButtonValue(aValue.getTristateVal())];
548 [pBtn setEnabled: getEnabled(nState, mpFrame)];
549 [pBtn setFocusRingType: NSFocusRingTypeExterior];
550 [pBtn setHighlighted: (nState & ControlState::PRESSED) ? YES : NO];
551 [pBtn setControlSize: eSizeKind];
552 if (nState & ControlState::DEFAULT)
553 [pBtn setKeyEquivalent: @"\r"];
554 else
555 [pBtn setKeyEquivalent: @""];
557 if (eBezelStyle == NSBezelStyleRounded)
559 int nMargin = RoundedMargin[eSizeKind];
560 rc.origin.x -= nMargin;
561 rc.size.width += nMargin * 2;
563 rc.origin.x += FOCUS_RING_WIDTH / 2;
564 rc.size.width -= FOCUS_RING_WIDTH;
567 const bool bFocused(nState & ControlState::FOCUSED);
568 paintCell(pBtn, rc, bFocused, context, nullptr);
570 bOK = true;
572 break;
573 case ControlType::Radiobutton:
574 case ControlType::Checkbox:
576 rc.size.width -= 2 * FOCUS_RING_WIDTH;
577 rc.size.height = RADIO_BUTTON_SMALL_SIZE;
578 rc.origin.x += FOCUS_RING_WIDTH;
579 rc.origin.y += FOCUS_RING_WIDTH;
581 NSButtonCell* pBtn = nType == ControlType::Checkbox ? pInst->mpCheckCell : pInst->mpRadioCell;
582 pBtn.allowsMixedState = YES;
584 [pBtn setTitle: @""];
585 [pBtn setButtonType: nType == ControlType::Checkbox ? NSButtonTypeSwitch : NSButtonTypeRadio];
586 [pBtn setState: ImplGetButtonValue(aValue.getTristateVal())];
587 [pBtn setEnabled: getEnabled(nState, mpFrame)];
588 [pBtn setFocusRingType: NSFocusRingTypeExterior];
589 [pBtn setHighlighted: (nState & ControlState::PRESSED) ? YES : NO];
591 const bool bFocused(nState & ControlState::FOCUSED);
592 paintCell(pBtn, rc, bFocused, context, nullptr);
594 bOK = true;
596 break;
597 case ControlType::ListNode:
599 NSButtonCell* pBtn = pInst->mpListNodeCell;
600 pBtn.allowsMixedState = YES;
602 [pBtn setTitle: @""];
603 [pBtn setButtonType: NSButtonTypeOnOff];
604 [pBtn setBezelStyle: NSBezelStyleDisclosure];
605 [pBtn setState: ImplGetButtonValue(aValue.getTristateVal())];
606 [pBtn setEnabled: getEnabled(nState, mpFrame)];
607 [pBtn setFocusRingType: NSFocusRingTypeExterior];
609 const bool bFocused(nState & ControlState::FOCUSED);
610 paintCell(pBtn, rc, bFocused, context, nullptr);
612 bOK = true;
614 break;
615 case ControlType::LevelBar:
617 NSRect rect = { NSZeroPoint, NSMakeSize(rc.size.width, rc.size.height) };
618 NSLevelIndicator* pBox = [[NSLevelIndicator alloc] initWithFrame:rect];
619 [pBox setLevelIndicatorStyle: NSLevelIndicatorStyleContinuousCapacity];
620 [pBox setMinValue: 0];
621 [pBox setMaxValue: rc.size.width];
622 [pBox setCriticalValue: rc.size.width * 35.0 / 100.0];
623 [pBox setWarningValue: rc.size.width * 70.0 / 100.0];
624 [pBox setDoubleValue: aValue.getNumericVal()];
626 CGContextSaveGState(context);
627 CGContextTranslateCTM(context, rc.origin.x, rc.origin.y);
629 NSGraphicsContext* savedContext = [NSGraphicsContext currentContext];
630 NSGraphicsContext* graphicsContext = [NSGraphicsContext graphicsContextWithCGContext:context flipped:NO];
631 [NSGraphicsContext setCurrentContext: graphicsContext];
633 [pBox drawRect: rect];
635 [NSGraphicsContext setCurrentContext: savedContext];
637 CGContextRestoreGState(context);
639 [pBox release];
641 bOK = true;
643 break;
644 case ControlType::Progress:
645 case ControlType::IntroProgress:
647 NSRect rect = { NSZeroPoint, NSMakeSize(rc.size.width, rc.size.height) };
648 NSProgressIndicator* pBox = [[NSProgressIndicator alloc] initWithFrame: rect];
649 [pBox setControlSize: (rc.size.height > MEDIUM_PROGRESS_INDICATOR_HEIGHT) ?
650 NSControlSizeRegular : NSControlSizeSmall];
651 [pBox setMinValue: 0];
652 [pBox setMaxValue: rc.size.width];
653 [pBox setDoubleValue: aValue.getNumericVal()];
654 pBox.usesThreadedAnimation = NO;
655 [pBox setIndeterminate: NO];
657 CGContextSaveGState(context);
658 CGContextTranslateCTM(context, rc.origin.x, rc.origin.y);
660 NSGraphicsContext* savedContext = [NSGraphicsContext currentContext];
661 NSGraphicsContext* graphicsContext = [NSGraphicsContext graphicsContextWithCGContext:context flipped:NO];
662 [NSGraphicsContext setCurrentContext: graphicsContext];
664 [pBox drawRect: rect];
666 [NSGraphicsContext setCurrentContext: savedContext];
668 CGContextRestoreGState(context);
670 [pBox release];
672 bOK = true;
674 break;
675 case ControlType::Slider:
677 const SliderValue *pSliderVal = static_cast<SliderValue const *>(&aValue);
678 if (nPart == ControlPart::TrackHorzArea || nPart == ControlPart::TrackVertArea)
680 NSRect rect = { NSZeroPoint, NSMakeSize(rc.size.width, rc.size.height) };
681 NSSlider* pBox = [[NSSlider alloc] initWithFrame: rect];
683 [pBox setEnabled: getEnabled(nState, mpFrame)];
684 [pBox setVertical: nPart == ControlPart::TrackVertArea];
685 [pBox setMinValue: pSliderVal->mnMin];
686 [pBox setMaxValue: pSliderVal->mnMax];
687 [pBox setIntegerValue: pSliderVal->mnCur];
688 [pBox setSliderType: NSSliderTypeLinear];
689 [pBox setFocusRingType: NSFocusRingTypeExterior];
691 const bool bFocused(nState & ControlState::FOCUSED);
692 paintCell(pBox.cell, rc, bFocused, context, mpFrame->getNSView());
694 [pBox release];
696 bOK = true;
699 break;
700 case ControlType::Scrollbar:
702 const ScrollbarValue *pScrollbarVal = (aValue.getType() == ControlType::Scrollbar)
703 ? static_cast<const ScrollbarValue *>(&aValue) : nullptr;
704 if (nPart == ControlPart::DrawBackgroundVert || nPart == ControlPart::DrawBackgroundHorz)
706 drawBox(context, rc, NSColor.controlBackgroundColor);
708 NSRect rect = { NSZeroPoint, NSMakeSize(rc.size.width, rc.size.height) };
709 NSScroller* pBar = [[NSScroller alloc] initWithFrame: rect];
711 double range = pScrollbarVal->mnMax - pScrollbarVal->mnVisibleSize - pScrollbarVal->mnMin;
712 double value = range ? (pScrollbarVal->mnCur - pScrollbarVal->mnMin) / range : 0;
714 double length = pScrollbarVal->mnMax - pScrollbarVal->mnMin;
715 double proportion = pScrollbarVal->mnVisibleSize / length;
717 [pBar setEnabled: getEnabled(nState, mpFrame)];
718 [pBar setScrollerStyle: NSScrollerStyleLegacy];
719 [pBar setFloatValue: value];
720 [pBar setKnobProportion: proportion];
721 bool bPressed = (pScrollbarVal->mnThumbState & ControlState::ENABLED) &&
722 (pScrollbarVal->mnThumbState & ControlState::PRESSED);
724 CGContextSaveGState(context);
725 CGContextTranslateCTM(context, rc.origin.x, rc.origin.y);
727 NSGraphicsContext* graphicsContext = [NSGraphicsContext graphicsContextWithCGContext:context flipped:NO];
729 NSGraphicsContext* savedContext = [NSGraphicsContext currentContext];
730 [NSGraphicsContext setCurrentContext: graphicsContext];
732 // For not-pressed first draw without the knob and then
733 // draw just the knob but with 50% opaque which looks sort of
734 // right
736 [pBar drawKnobSlotInRect: rect highlight: NO];
738 NSBitmapImageRep* pImageRep = [pBar bitmapImageRepForCachingDisplayInRect: rect];
740 NSGraphicsContext* imageContext = [NSGraphicsContext graphicsContextWithBitmapImageRep:pImageRep];
741 [NSGraphicsContext setCurrentContext: imageContext];
743 [pBar drawKnob];
745 [NSGraphicsContext setCurrentContext: graphicsContext];
747 NSImage* pImage = [[NSImage alloc] initWithSize: rect.size];
748 [pImage addRepresentation: pImageRep]; // takes ownership of pImageRep
750 [pImage drawInRect: rect fromRect: rect
751 operation: NSCompositingOperationSourceOver
752 fraction: bPressed ? 1.0 : 0.5];
754 [pImage release];
756 [NSGraphicsContext setCurrentContext:savedContext];
758 CGContextRestoreGState(context);
760 bOK = true;
762 [pBar release];
765 break;
766 case ControlType::TabPane:
768 NSTabView* pBox = [[NSTabView alloc] initWithFrame: rc];
770 SInt32 nOverlap;
771 GetThemeMetric(kThemeMetricTabFrameOverlap, &nOverlap);
773 // this calculation is probably more than a little dubious
774 rc.origin.x -= pBox.contentRect.origin.x - FOCUS_RING_WIDTH;
775 rc.size.width += rc.size.width - pBox.contentRect.size.width - 2 * FOCUS_RING_WIDTH;
776 double nTopBorder = pBox.contentRect.origin.y;
777 double nBottomBorder = rc.size.height - pBox.contentRect.size.height - nTopBorder;
778 double nExtraTop = (nTopBorder - nBottomBorder) / 2;
779 rc.origin.y -= (nTopBorder - nExtraTop + nOverlap);
780 rc.size.height += (nTopBorder - nExtraTop + nBottomBorder);
782 CGContextSaveGState(context);
783 CGContextTranslateCTM(context, rc.origin.x, rc.origin.y);
785 rc.origin.x = 0;
786 rc.origin.y = 0;
788 [pBox setBoundsOrigin: rc.origin];
789 [pBox setBoundsSize: rc.size];
791 // jam this in to force the tab contents area to be left undrawn, the ControlType::TabItem
792 // will be drawn in this space.
793 const TabPaneValue& rValue = static_cast<const TabPaneValue&>(aValue);
794 SInt32 nEndCapWidth;
795 GetThemeMetric(kThemeMetricLargeTabCapsWidth, &nEndCapWidth);
796 FixedWidthTabViewItem* pItem = [[[FixedWidthTabViewItem alloc] initWithIdentifier: @"tab"] autorelease];
797 [pItem setTabWidth: rValue.m_aTabHeaderRect.GetWidth() - 2 * nEndCapWidth];
798 [pBox addTabViewItem: pItem];
800 NSGraphicsContext* graphicsContext = [NSGraphicsContext graphicsContextWithCGContext:context flipped:NO];
802 NSGraphicsContext* savedContext = [NSGraphicsContext currentContext];
803 [NSGraphicsContext setCurrentContext: graphicsContext];
805 [pBox drawRect: rc];
807 [NSGraphicsContext setCurrentContext: savedContext];
809 [pBox release];
811 CGContextRestoreGState(context);
813 bOK = true;
815 break;
816 case ControlType::TabItem:
818 // first, last or middle tab
820 TabitemValue const * pTabValue = static_cast<TabitemValue const *>(&aValue);
821 TabitemFlags nAlignment = pTabValue->mnAlignment;
823 // TabitemFlags::LeftAligned (and TabitemFlags::RightAligned) for the leftmost (or rightmost) tab
824 // when there are several lines of tabs because there is only one first tab and one
825 // last tab and TabitemFlags::FirstInGroup (and TabitemFlags::LastInGroup) because when the
826 // line width is different from window width, there may not be TabitemFlags::RightAligned
827 int nPaintIndex = 1;
828 bool bSolo = false;
829 if (((nAlignment & TabitemFlags::LeftAligned) && (nAlignment & TabitemFlags::RightAligned))
830 || ((nAlignment & TabitemFlags::FirstInGroup) && (nAlignment & TabitemFlags::LastInGroup)))
832 nPaintIndex = 0;
833 bSolo = true;
835 else if ((nAlignment & TabitemFlags::LeftAligned) || (nAlignment & TabitemFlags::FirstInGroup))
836 nPaintIndex = !AllSettings::GetLayoutRTL() ? 0 : 2;
837 else if ((nAlignment & TabitemFlags::RightAligned) || (nAlignment & TabitemFlags::LastInGroup))
838 nPaintIndex = !AllSettings::GetLayoutRTL() ? 2 : 0;
840 int nCells = !bSolo ? 3 : 1;
841 NSRect ctrlrect = { NSZeroPoint, NSMakeSize(rc.size.width * nCells + FOCUS_RING_WIDTH, rc.size.height) };
842 NSSegmentedControl* pCtrl = [[NSSegmentedControl alloc] initWithFrame: ctrlrect];
843 [pCtrl setSegmentCount: nCells];
844 if (bSolo)
845 [pCtrl setWidth: rc.size.width + FOCUS_RING_WIDTH forSegment: 0];
846 else
848 [pCtrl setWidth: rc.size.width + FOCUS_RING_WIDTH/2 forSegment: 0];
849 [pCtrl setWidth: rc.size.width forSegment: 1];
850 [pCtrl setWidth: rc.size.width + FOCUS_RING_WIDTH/2 forSegment: 2];
852 [pCtrl setSelected: (nState & ControlState::SELECTED) ? YES : NO forSegment: nPaintIndex];
853 [pCtrl setFocusRingType: NSFocusRingTypeExterior];
855 NSGraphicsContext* savedContext = [NSGraphicsContext currentContext];
856 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithCGContext:context flipped:NO]];
858 NSRect rect = { NSZeroPoint, NSMakeSize(rc.size.width, rc.size.height) };
859 NSRect tabrect = { NSMakePoint(rc.size.width * nPaintIndex + FOCUS_RING_WIDTH / 2, 0),
860 NSMakeSize(rc.size.width, rc.size.height) };
861 NSBitmapImageRep* pImageRep = [pCtrl bitmapImageRepForCachingDisplayInRect: tabrect];
862 [pCtrl cacheDisplayInRect: tabrect toBitmapImageRep: pImageRep];
864 NSImage* pImage = [[NSImage alloc] initWithSize: rect.size];
865 [pImage addRepresentation: pImageRep]; // takes ownership of pImageRep
867 [pImage drawInRect: rc fromRect: rect
868 operation: NSCompositingOperationSourceOver
869 fraction: 1.0];
871 [pImage release];
873 [NSGraphicsContext setCurrentContext:savedContext];
875 [pCtrl release];
877 if (nState & ControlState::FOCUSED)
879 if (!bSolo)
881 if (nPaintIndex == 0)
883 rc.origin.x += FOCUS_RING_WIDTH / 2;
884 rc.size.width -= FOCUS_RING_WIDTH / 2;
886 else if (nPaintIndex == 2)
888 rc.size.width -= FOCUS_RING_WIDTH / 2;
889 rc.size.width -= FOCUS_RING_WIDTH / 2;
893 paintFocusRect(4.0, rc, context);
895 bOK=true;
897 break;
898 case ControlType::Editbox:
899 case ControlType::MultilineEditbox:
901 rc.size.width += 2 * EDITBOX_INSET_MARGIN;
902 if (nType == ControlType::Editbox)
903 rc.size.height = EDITBOX_HEIGHT;
904 else
905 rc.size.height += 2 * (EDITBOX_BORDER_WIDTH + EDITBOX_INSET_MARGIN);
906 rc.origin.x -= EDITBOX_INSET_MARGIN;
907 rc.origin.y -= EDITBOX_INSET_MARGIN;
909 NSTextFieldCell* pBtn = pInst->mpTextFieldCell;
911 [pBtn setEnabled: getEnabled(nState, mpFrame)];
912 [pBtn setBezeled: YES];
913 [pBtn setEditable: YES];
914 [pBtn setFocusRingType: NSFocusRingTypeExterior];
916 drawEditableBackground(context, rc);
917 const bool bFocused(nState & ControlState::FOCUSED);
918 paintCell(pBtn, rc, bFocused, context, mpFrame->getNSView());
920 bOK = true;
922 break;
923 case ControlType::Combobox:
924 if (nPart == ControlPart::HasBackgroundTexture || nPart == ControlPart::Entire)
926 rc.origin.y += (rc.size.height - COMBOBOX_HEIGHT + 1) / 2;
927 rc.size.height = COMBOBOX_HEIGHT;
929 NSComboBoxCell* pBtn = pInst->mpComboBoxCell;
931 [pBtn setEnabled: getEnabled(nState, mpFrame)];
932 [pBtn setEditable: YES];
933 [pBtn setState: ImplGetButtonValue(aValue.getTristateVal())];
934 [pBtn setFocusRingType: NSFocusRingTypeExterior];
937 rc.origin.x += 2;
938 rc.size.width -= 1;
941 drawEditableBackground(context, rc);
942 const bool bFocused(nState & ControlState::FOCUSED);
943 paintCell(pBtn, rc, bFocused, context, mpFrame->getNSView());
945 bOK = true;
947 break;
948 case ControlType::Listbox:
950 switch (nPart)
952 case ControlPart::Entire:
953 case ControlPart::ButtonDown:
955 rc.origin.y += (rc.size.height - LISTBOX_HEIGHT + 1) / 2;
956 rc.size.height = LISTBOX_HEIGHT;
958 NSPopUpButtonCell* pBtn = pInst->mpPopUpButtonCell;
960 [pBtn setTitle: @""];
961 [pBtn setEnabled: getEnabled(nState, mpFrame)];
962 [pBtn setFocusRingType: NSFocusRingTypeExterior];
963 [pBtn setHighlighted: (nState & ControlState::PRESSED) ? YES : NO];
964 if (nState & ControlState::DEFAULT)
965 [pBtn setKeyEquivalent: @"\r"];
966 else
967 [pBtn setKeyEquivalent: @""];
970 rc.size.width += 1;
973 const bool bFocused(nState & ControlState::FOCUSED);
974 paintCell(pBtn, rc, bFocused, context, nullptr);
976 bOK = true;
977 break;
979 case ControlPart::ListboxWindow:
981 NSRect rect = { NSZeroPoint, NSMakeSize(rc.size.width, rc.size.height) };
982 NSScrollView* pBox = [[NSScrollView alloc] initWithFrame: rect];
983 [pBox setBorderType: NSLineBorder];
985 CGContextSaveGState(context);
986 CGContextTranslateCTM(context, rc.origin.x, rc.origin.y);
988 NSGraphicsContext* savedContext = [NSGraphicsContext currentContext];
989 NSGraphicsContext* graphicsContext = [NSGraphicsContext graphicsContextWithCGContext:context flipped:NO];
990 [NSGraphicsContext setCurrentContext: graphicsContext];
992 [pBox drawRect: rect];
994 [NSGraphicsContext setCurrentContext: savedContext];
996 CGContextRestoreGState(context);
998 [pBox release];
1000 bOK = true;
1001 break;
1003 default:
1004 break;
1006 break;
1007 case ControlType::Spinbox:
1008 if (nPart == ControlPart::Entire)
1010 // text field
1012 rc.size.width -= SPIN_BUTTON_WIDTH + 4 * FOCUS_RING_WIDTH;
1013 rc.size.height = EDITBOX_HEIGHT;
1014 rc.origin.x += FOCUS_RING_WIDTH;
1015 rc.origin.y += FOCUS_RING_WIDTH;
1017 NSTextFieldCell* pEdit = pInst->mpTextFieldCell;
1019 [pEdit setEnabled: YES];
1020 [pEdit setBezeled: YES];
1021 [pEdit setEditable: YES];
1022 [pEdit setFocusRingType: NSFocusRingTypeExterior];
1024 drawEditableBackground(context, rc);
1025 const bool bFocused(nState & ControlState::FOCUSED);
1026 paintCell(pEdit, rc, bFocused, context, mpFrame->getNSView());
1028 // buttons
1030 const SpinbuttonValue *pSpinButtonVal = (aValue.getType() == ControlType::SpinButtons)
1031 ? static_cast <const SpinbuttonValue *>(&aValue) : nullptr;
1032 if (pSpinButtonVal)
1034 ControlState nUpperState = pSpinButtonVal->mnUpperState;
1035 ControlState nLowerState = pSpinButtonVal->mnLowerState;
1037 rc.origin.x += rc.size.width + FOCUS_RING_WIDTH + 1;
1038 rc.origin.y -= 1;
1039 rc.size.width = SPIN_BUTTON_WIDTH;
1040 rc.size.height = SPIN_LOWER_BUTTON_HEIGHT + SPIN_LOWER_BUTTON_HEIGHT;
1042 NSStepperCell* pBtn = pInst->mpStepperCell;
1044 [pBtn setTitle: @""];
1045 [pBtn setState: ImplGetButtonValue(aValue.getTristateVal())];
1046 [pBtn setEnabled: (nUpperState & ControlState::ENABLED || nLowerState & ControlState::ENABLED) ?
1047 YES : NO];
1048 [pBtn setFocusRingType: NSFocusRingTypeExterior];
1049 [pBtn setHighlighted: (nState & ControlState::PRESSED) ? YES : NO];
1051 const bool bSpinFocused(nUpperState & ControlState::FOCUSED || nLowerState & ControlState::FOCUSED);
1052 paintCell(pBtn, rc, bSpinFocused, context, nullptr);
1054 bOK = true;
1056 break;
1057 case ControlType::Frame:
1059 DrawFrameFlags nStyle = static_cast<DrawFrameFlags>(aValue.getNumericVal());
1060 if (nPart == ControlPart::Border)
1062 if (!(nStyle & DrawFrameFlags::Menu) && !(nStyle & DrawFrameFlags::WindowBorder))
1065 // strange effects start to happen when HIThemeDrawFrame meets the border of the window.
1066 // These can be avoided by clipping to the boundary of the frame (see issue 84756)
1068 if (rc.origin.y + rc.size.height >= mpFrame->maGeometry.height() - 3)
1070 CGMutablePathRef rPath = CGPathCreateMutable();
1071 CGPathAddRect(rPath, nullptr,
1072 CGRectMake(0, 0, mpFrame->maGeometry.width() - 1, mpFrame->maGeometry.height() - 1));
1073 CGContextBeginPath(context);
1074 CGContextAddPath(context, rPath);
1075 CGContextClip(context);
1076 CGPathRelease(rPath);
1078 HIThemeFrameDrawInfo aTextDrawInfo;
1079 aTextDrawInfo.version = 0;
1080 aTextDrawInfo.kind = kHIThemeFrameListBox;
1081 aTextDrawInfo.state = kThemeStateActive;
1082 aTextDrawInfo.isFocused = false;
1083 HIThemeDrawFrame(&rc, &aTextDrawInfo, context, kHIThemeOrientationNormal);
1084 bOK = true;
1088 break;
1089 case ControlType::ListNet:
1091 // do nothing as there isn't net for listviews on macOS
1093 bOK = true;
1094 break;
1095 default:
1096 break;
1099 return bOK;
1102 bool AquaSalGraphics::getNativeControlRegion(ControlType nType,
1103 ControlPart nPart,
1104 const tools::Rectangle &rControlRegion,
1105 ControlState,
1106 const ImplControlValue &aValue,
1107 const OUString &,
1108 tools::Rectangle &rNativeBoundingRegion,
1109 tools::Rectangle &rNativeContentRegion)
1111 bool toReturn = false;
1112 tools::Rectangle aCtrlBoundRect(rControlRegion);
1113 short x = aCtrlBoundRect.Left();
1114 short y = aCtrlBoundRect.Top();
1115 short w, h;
1116 switch (nType)
1118 case ControlType::Pushbutton:
1119 case ControlType::Radiobutton:
1120 case ControlType::Checkbox:
1122 if (nType == ControlType::Pushbutton)
1124 w = aCtrlBoundRect.GetWidth();
1125 h = aCtrlBoundRect.GetHeight();
1127 else
1129 w = RADIO_BUTTON_SMALL_SIZE + 2 * FOCUS_RING_WIDTH + RADIO_BUTTON_TEXT_SEPARATOR;
1130 h = RADIO_BUTTON_SMALL_SIZE + 2 * FOCUS_RING_WIDTH;
1132 rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h));
1133 rNativeBoundingRegion = tools::Rectangle(Point(x, y), Size(w, h));
1134 toReturn = true;
1136 break;
1137 case ControlType::LevelBar:
1138 case ControlType::Progress:
1140 tools::Rectangle aRect(aCtrlBoundRect);
1141 if (aRect.GetHeight() < LARGE_PROGRESS_INDICATOR_HEIGHT)
1142 aRect.SetBottom(aRect.Top() + MEDIUM_PROGRESS_INDICATOR_HEIGHT - 1);
1143 else
1144 aRect.SetBottom(aRect.Top() + LARGE_PROGRESS_INDICATOR_HEIGHT - 1);
1145 rNativeBoundingRegion = aRect;
1146 rNativeContentRegion = aRect;
1147 toReturn = true;
1149 break;
1150 case ControlType::IntroProgress:
1152 tools::Rectangle aRect(aCtrlBoundRect);
1153 aRect.SetBottom(aRect.Top() + MEDIUM_PROGRESS_INDICATOR_HEIGHT - 1);
1154 rNativeBoundingRegion = aRect;
1155 rNativeContentRegion = aRect;
1156 toReturn = true;
1158 break;
1159 case ControlType::Slider:
1160 if (nPart == ControlPart::ThumbHorz)
1162 w = SLIDER_WIDTH;
1163 h = aCtrlBoundRect.GetHeight();
1164 rNativeBoundingRegion = rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h));
1165 toReturn = true;
1167 else if (nPart == ControlPart::ThumbVert)
1169 w = aCtrlBoundRect.GetWidth();
1170 h = SLIDER_HEIGHT;
1171 rNativeBoundingRegion = rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h));
1172 toReturn = true;
1174 break;
1175 case ControlType::Scrollbar:
1177 tools::Rectangle aRect;
1178 if (AquaGetScrollRect(nPart, aCtrlBoundRect, aRect))
1180 toReturn = true;
1181 rNativeBoundingRegion = aRect;
1182 rNativeContentRegion = aRect;
1185 break;
1186 case ControlType::TabItem:
1188 w = aCtrlBoundRect.GetWidth() + 2 * TAB_TEXT_MARGIN;
1189 h = TAB_HEIGHT + 2;
1190 rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h));
1191 rNativeBoundingRegion = tools::Rectangle(Point(x, y), Size(w, h));
1192 toReturn = true;
1194 break;
1195 case ControlType::Editbox:
1197 const tools::Long nBorderThickness = FOCUS_RING_WIDTH + EDITBOX_BORDER_WIDTH + EDITBOX_INSET_MARGIN;
1198 // tdf#144241 don't return a negative width, expand the region to the min osx width
1199 w = std::max(nBorderThickness * 2, aCtrlBoundRect.GetWidth());
1200 h = EDITBOX_HEIGHT + 2 * FOCUS_RING_WIDTH;
1201 rNativeBoundingRegion = tools::Rectangle(Point(x, y), Size(w, h));
1202 w -= 2 * nBorderThickness;
1203 h -= 2 * nBorderThickness;
1204 x += nBorderThickness;
1205 y += nBorderThickness;
1206 rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h));
1207 toReturn = true;
1209 break;
1210 case ControlType::Combobox:
1211 if (nPart == ControlPart::Entire)
1213 w = aCtrlBoundRect.GetWidth();
1214 h = COMBOBOX_HEIGHT + 2 * FOCUS_RING_WIDTH;
1215 rNativeBoundingRegion = tools::Rectangle(Point(x, y), Size(w, h));
1216 rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h));
1217 toReturn = true;
1219 else if (nPart == ControlPart::ButtonDown)
1221 w = COMBOBOX_BUTTON_WIDTH + FOCUS_RING_WIDTH;
1222 h = COMBOBOX_HEIGHT + 2 * FOCUS_RING_WIDTH;
1223 x += aCtrlBoundRect.GetWidth() - w;
1224 rNativeBoundingRegion = tools::Rectangle(Point(x, y), Size(w, h));
1225 rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h));
1226 toReturn = true;
1228 else if (nPart == ControlPart::SubEdit)
1230 w = aCtrlBoundRect.GetWidth() - 2 * FOCUS_RING_WIDTH - COMBOBOX_BUTTON_WIDTH - COMBOBOX_BORDER_WIDTH
1231 - 2 * COMBOBOX_TEXT_MARGIN;
1232 h = COMBOBOX_HEIGHT - 2 * COMBOBOX_BORDER_WIDTH;
1233 x += FOCUS_RING_WIDTH + COMBOBOX_BORDER_WIDTH + COMBOBOX_TEXT_MARGIN;
1234 y += FOCUS_RING_WIDTH + COMBOBOX_BORDER_WIDTH;
1235 rNativeBoundingRegion = tools::Rectangle(Point(x, y), Size(w, h));
1236 rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h));
1237 toReturn = true;
1239 break;
1240 case ControlType::Listbox:
1241 if (nPart == ControlPart::Entire)
1243 w = aCtrlBoundRect.GetWidth();
1244 h = LISTBOX_HEIGHT + 2 * FOCUS_RING_WIDTH;
1245 rNativeBoundingRegion = tools::Rectangle(Point(x, y), Size(w, h));
1246 rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h));
1247 toReturn = true;
1249 else if (nPart == ControlPart::ButtonDown)
1251 w = LISTBOX_BUTTON_WIDTH + FOCUS_RING_WIDTH;
1252 h = LISTBOX_HEIGHT + 2 * FOCUS_RING_WIDTH;
1253 x += aCtrlBoundRect.GetWidth() - w;
1254 rNativeBoundingRegion = tools::Rectangle(Point(x, y), Size(w, h));
1255 rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h));
1256 toReturn = true;
1258 else if (nPart == ControlPart::SubEdit)
1260 w = aCtrlBoundRect.GetWidth() - 2 * FOCUS_RING_WIDTH - LISTBOX_BUTTON_WIDTH - LISTBOX_BORDER_WIDTH
1261 - 2 * LISTBOX_TEXT_MARGIN;
1262 h = LISTBOX_HEIGHT - 2 * LISTBOX_BORDER_WIDTH;
1263 x += FOCUS_RING_WIDTH + LISTBOX_BORDER_WIDTH + LISTBOX_TEXT_MARGIN;
1264 y += FOCUS_RING_WIDTH + LISTBOX_BORDER_WIDTH;
1265 rNativeBoundingRegion = tools::Rectangle(Point(x, y), Size(w, h));
1266 rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h));
1267 toReturn = true;
1269 else if (nPart == ControlPart::ListboxWindow)
1271 w = aCtrlBoundRect.GetWidth() - 2;
1272 h = aCtrlBoundRect.GetHeight() - 2;
1273 x += 1;
1274 y += 1;
1275 rNativeBoundingRegion = aCtrlBoundRect;
1276 rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h));
1277 toReturn = true;
1279 break;
1280 case ControlType::Spinbox:
1281 if (nPart == ControlPart::Entire)
1283 w = aCtrlBoundRect.GetWidth();
1284 h = EDITBOX_HEIGHT + 2 * FOCUS_RING_WIDTH;
1285 x += SPINBOX_OFFSET;
1286 rNativeBoundingRegion = tools::Rectangle(Point(x, y), Size(w, h));
1287 rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h));
1288 toReturn = true;
1290 else if (nPart == ControlPart::SubEdit)
1292 w = aCtrlBoundRect.GetWidth() - 4 * FOCUS_RING_WIDTH - SPIN_BUTTON_WIDTH - 2 * EDITBOX_BORDER_WIDTH
1293 - 2 * EDITBOX_INSET_MARGIN;
1294 h = EDITBOX_HEIGHT - 2 * (EDITBOX_BORDER_WIDTH + EDITBOX_INSET_MARGIN);
1295 x += FOCUS_RING_WIDTH + EDITBOX_BORDER_WIDTH + EDITBOX_INSET_MARGIN + SPINBOX_OFFSET;
1296 y += FOCUS_RING_WIDTH + EDITBOX_BORDER_WIDTH + EDITBOX_INSET_MARGIN;
1297 rNativeBoundingRegion = tools::Rectangle(Point(x, y), Size(w, h));
1298 rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h));
1299 toReturn = true;
1301 else if (nPart == ControlPart::ButtonUp)
1303 w = SPIN_BUTTON_WIDTH + 2 * FOCUS_RING_WIDTH;
1304 h = SPIN_UPPER_BUTTON_HEIGHT + FOCUS_RING_WIDTH;
1305 x += aCtrlBoundRect.GetWidth() - SPIN_BUTTON_WIDTH - 2 * FOCUS_RING_WIDTH + SPINBOX_OFFSET;
1306 rNativeBoundingRegion = tools::Rectangle(Point(x, y), Size(w, h));
1307 rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h));
1308 toReturn = true;
1310 else if (nPart == ControlPart::ButtonDown)
1312 w = SPIN_BUTTON_WIDTH + 2 * FOCUS_RING_WIDTH;
1313 h = SPIN_LOWER_BUTTON_HEIGHT + FOCUS_RING_WIDTH;
1314 x += aCtrlBoundRect.GetWidth() - SPIN_BUTTON_WIDTH - 2 * FOCUS_RING_WIDTH + SPINBOX_OFFSET;
1315 y += FOCUS_RING_WIDTH + SPIN_UPPER_BUTTON_HEIGHT;
1316 rNativeBoundingRegion = tools::Rectangle(Point(x, y), Size(w, h));
1317 rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h));
1318 toReturn = true;
1320 break;
1321 case ControlType::Frame:
1323 DrawFrameStyle nStyle = static_cast<DrawFrameStyle>(aValue.getNumericVal() & 0x000f);
1324 DrawFrameFlags nFlags = static_cast<DrawFrameFlags>(aValue.getNumericVal() & 0xfff0);
1325 if (nPart == ControlPart::Border
1326 && !(nFlags & (DrawFrameFlags::Menu | DrawFrameFlags::WindowBorder | DrawFrameFlags::BorderWindowBorder)))
1328 tools::Rectangle aRect(aCtrlBoundRect);
1329 if (nStyle == DrawFrameStyle::DoubleIn)
1331 aRect.AdjustLeft(1);
1332 aRect.AdjustTop(1);
1333 // rRect.Right() -= 1;
1334 // rRect.Bottom() -= 1;
1336 else
1338 aRect.AdjustLeft(1);
1339 aRect.AdjustTop(1);
1340 aRect.AdjustRight(-1);
1341 aRect.AdjustBottom(-1);
1343 rNativeContentRegion = aRect;
1344 rNativeBoundingRegion = aRect;
1345 toReturn = true;
1348 break;
1349 case ControlType::Menubar:
1350 case ControlType::MenuPopup:
1351 if (nPart == ControlPart::MenuItemCheckMark || nPart == ControlPart::MenuItemRadioMark)
1353 w=10;
1354 h=10;
1355 rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h));
1356 rNativeBoundingRegion = tools::Rectangle(Point(x, y), Size(w, h));
1357 toReturn = true;
1359 break;
1360 default:
1361 break;
1363 return toReturn;
1366 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */