diff options
Diffstat (limited to 'vcl/osx/salnativewidgets.cxx')
-rw-r--r-- | vcl/osx/salnativewidgets.cxx | 1366 |
1 files changed, 1366 insertions, 0 deletions
diff --git a/vcl/osx/salnativewidgets.cxx b/vcl/osx/salnativewidgets.cxx new file mode 100644 index 0000000000..8a7e81fd5d --- /dev/null +++ b/vcl/osx/salnativewidgets.cxx @@ -0,0 +1,1366 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <config_features.h> +#include <tools/long.hxx> +#include <vcl/salnativewidgets.hxx> +#include <vcl/decoview.hxx> +#include <vcl/svapp.hxx> +#include <vcl/threadex.hxx> +#include <vcl/timer.hxx> +#include <vcl/settings.hxx> + +#include <quartz/salgdi.h> +#include <osx/salnativewidgets.h> +#include <osx/saldata.hxx> +#include <osx/salframe.h> + +#include <premac.h> +#include <Carbon/Carbon.h> +#include <postmac.h> + +#include "cuidraw.hxx" + +// presentation of native widgets consists of two important methods: + +// AquaSalGraphics::getNativeControlRegion to determine native rectangle in pixels to draw the widget +// AquaSalGraphics::drawNativeControl to do the drawing operation itself + +// getNativeControlRegion has to calculate a content rectangle within it is safe to draw the widget. Furthermore a bounding rectangle +// has to be calculated by getNativeControlRegion to consider adornments like a focus rectangle. As drawNativeControl uses Carbon +// API calls, all widgets are drawn without text. Drawing of text is done separately by VCL on top of graphical Carbon widget +// representation. drawNativeControl is called by VCL using content rectangle determined by getNativeControlRegion. + +// FIXME: when calculation bounding rectangle larger then content rectangle, text displayed by VCL will become misaligned. To avoid +// misalignment bounding rectangle and content rectangle are calculated equally including adornments. Reduction of size for content +// is done by drawNativeControl subsequently. Only exception is editbox: As other widgets have distinct ControlPart::SubEdit control +// parts, editbox bounding rectangle and content rectangle are both calculated to reflect content area. Extending size for +// adornments is done by drawNativeControl subsequently. + +#if !HAVE_FEATURE_MACOSX_SANDBOX + +@interface NSWindow(CoreUIRendererPrivate) ++ (CUIRendererRef)coreUIRenderer; +@end + +#endif + +static HIRect ImplGetHIRectFromRectangle(tools::Rectangle aRect) +{ + HIRect aHIRect; + aHIRect.origin.x = static_cast<float>(aRect.Left()); + aHIRect.origin.y = static_cast<float>(aRect.Top()); + aHIRect.size.width = static_cast<float>(aRect.GetWidth()); + aHIRect.size.height = static_cast<float>(aRect.GetHeight()); + return aHIRect; +} + +static NSControlStateValue ImplGetButtonValue(ButtonValue aButtonValue) +{ + switch (aButtonValue) + { + case ButtonValue::On: + return NSControlStateValueOn; + case ButtonValue::Off: + case ButtonValue::DontKnow: + return NSControlStateValueOff; + case ButtonValue::Mixed: + default: + return NSControlStateValueMixed; + } +} + +static bool AquaGetScrollRect(/* TODO: int nScreen, */ + ControlPart nPart, const tools::Rectangle &rControlRect, tools::Rectangle &rResultRect) +{ + bool bRetVal = true; + rResultRect = rControlRect; + switch (nPart) + { + case ControlPart::ButtonUp: + rResultRect.SetBottom(rResultRect.Top()); + break; + case ControlPart::ButtonDown: + rResultRect.SetTop(rResultRect.Bottom()); + break; + case ControlPart::ButtonLeft: + rResultRect.SetRight(rResultRect.Left()); + break; + case ControlPart::ButtonRight: + rResultRect.SetLeft(rResultRect.Right()); + break; + case ControlPart::TrackHorzArea: + case ControlPart::TrackVertArea: + case ControlPart::ThumbHorz: + case ControlPart::ThumbVert: + case ControlPart::TrackHorzLeft: + case ControlPart::TrackHorzRight: + case ControlPart::TrackVertUpper: + case ControlPart::TrackVertLower: + break; + default: + bRetVal = false; + } + return bRetVal; +} + +bool AquaSalGraphics::isNativeControlSupported(ControlType nType, ControlPart nPart) +{ + // native controls are now defaults. If you want to disable native controls, set the environment variable SAL_NO_NWF to + // something and VCL controls will be used as default again. + + switch (nType) + { + case ControlType::Pushbutton: + case ControlType::Radiobutton: + case ControlType::Checkbox: + case ControlType::ListNode: + if (nPart == ControlPart::Entire) + return true; + break; + case ControlType::Scrollbar: + if (nPart == ControlPart::DrawBackgroundHorz || nPart == ControlPart::DrawBackgroundVert + || nPart == ControlPart::Entire || nPart == ControlPart::HasThreeButtons) + return true; + break; + case ControlType::Slider: + if (nPart == ControlPart::TrackHorzArea || nPart == ControlPart::TrackVertArea) + return true; + break; + case ControlType::Editbox: + if (nPart == ControlPart::Entire || nPart == ControlPart::HasBackgroundTexture) + return true; + break; + case ControlType::MultilineEditbox: + if (nPart == ControlPart::Entire || nPart == ControlPart::HasBackgroundTexture) + return true; + break; + case ControlType::Spinbox: + if (nPart == ControlPart::Entire || nPart == ControlPart::AllButtons || nPart == ControlPart::HasBackgroundTexture) + return true; + break; + case ControlType::SpinButtons: + return false; + case ControlType::Combobox: + if (nPart == ControlPart::Entire || nPart == ControlPart::HasBackgroundTexture) + return true; + break; + case ControlType::Listbox: + if (nPart == ControlPart::Entire || nPart == ControlPart::ListboxWindow || nPart == ControlPart::HasBackgroundTexture + || nPart == ControlPart::SubEdit) + return true; + break; + case ControlType::TabItem: + case ControlType::TabPane: + case ControlType::TabBody: + if (nPart == ControlPart::Entire || nPart == ControlPart::TabsDrawRtl || nPart == ControlPart::HasBackgroundTexture) + return true; + break; + case ControlType::Toolbar: + if (nPart == ControlPart::Entire || nPart == ControlPart::DrawBackgroundHorz + || nPart == ControlPart::DrawBackgroundVert) + return true; + break; + case ControlType::WindowBackground: + if (nPart == ControlPart::BackgroundWindow || nPart == ControlPart::BackgroundDialog) + return true; + break; + case ControlType::Menubar: + if (nPart == ControlPart::Entire) + return true; + break; + case ControlType::Tooltip: + if (nPart == ControlPart::Entire) + return true; + break; + case ControlType::MenuPopup: + if (nPart == ControlPart::Entire || nPart == ControlPart::MenuItem || nPart == ControlPart::MenuItemCheckMark + || nPart == ControlPart::MenuItemRadioMark) + return true; + break; + case ControlType::LevelBar: + case ControlType::Progress: + case ControlType::IntroProgress: + if (nPart == ControlPart::Entire) + return true; + break; + case ControlType::Frame: + if (nPart == ControlPart::Border) + return true; + break; + case ControlType::ListNet: + if (nPart == ControlPart::Entire) + return true; + break; + default: + break; + } + return false; +} + +bool AquaSalGraphics::hitTestNativeControl(ControlType nType, ControlPart nPart, const tools::Rectangle &rControlRegion, + const Point &rPos, bool& rIsInside) +{ + if (nType == ControlType::Scrollbar) + { + tools::Rectangle aRect; + bool bValid = AquaGetScrollRect(/* TODO: int nScreen, */ + nPart, rControlRegion, aRect); + rIsInside = bValid && aRect.Contains(rPos); + return bValid; + } + return false; +} + +static bool getEnabled(ControlState nState, AquaSalFrame* mpFrame) +{ + + // there are non key windows which are children of key windows, e.g. autofilter configuration dialog or sidebar dropdown dialogs. + // To handle these windows correctly, parent frame's key window state is considered here additionally. + + const bool bDrawActive = mpFrame == nullptr || [mpFrame->getNSWindow() isKeyWindow] + || mpFrame->mpParent == nullptr || [mpFrame->mpParent->getNSWindow() isKeyWindow]; + if (!(nState & ControlState::ENABLED) || !bDrawActive) + { + return false; + } + return true; +} + +bool AquaSalGraphics::drawNativeControl(ControlType nType, + ControlPart nPart, + const tools::Rectangle &rControlRegion, + ControlState nState, + const ImplControlValue &aValue, + const OUString &, + const Color&) +{ + return mpBackend->drawNativeControl(nType, nPart, rControlRegion, nState, aValue); +} + +static void paintCell(NSCell* pBtn, const NSRect& bounds, bool bShowsFirstResponder, CGContextRef context, NSView* pView) +{ + //translate and scale because up side down otherwise + CGContextSaveGState(context); + CGContextTranslateCTM(context, bounds.origin.x, bounds.origin.y + bounds.size.height); + CGContextScaleCTM(context, 1, -1); + + NSGraphicsContext* savedContext = [NSGraphicsContext currentContext]; + [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithCGContext:context flipped:NO]]; + + NSRect rect = { NSZeroPoint, bounds.size }; + + if ([pBtn isKindOfClass: [NSSliderCell class]]) + { + // NSSliderCell doesn't seem to work with drawWithFrame(?), so draw the elements directly + [static_cast<NSSliderCell*>(pBtn) + drawBarInside: [static_cast<NSSliderCell*>(pBtn) barRectFlipped: NO] flipped: NO]; + rect = [static_cast<NSSliderCell*>(pBtn) knobRectFlipped: NO]; + [static_cast<NSSliderCell*>(pBtn) drawKnob: rect]; + } + else + [pBtn drawWithFrame: rect inView: pView]; + + // setShowsFirstResponder apparently causes a hang when set on NSComboBoxCell + const bool bIsComboBox = [pBtn isMemberOfClass: [NSComboBoxCell class]]; + if (!bIsComboBox) + [pBtn setShowsFirstResponder: bShowsFirstResponder]; + + if (bShowsFirstResponder) + { + NSSetFocusRingStyle(NSFocusRingOnly); + + CGContextBeginTransparencyLayerWithRect(context, rect, nullptr); + if ([pBtn isMemberOfClass: [NSTextFieldCell class]]) + { + // I wonder why NSTextFieldCell doesn't work for me in the default else branch. + // NSComboBoxCell works, and that derives from NSTextFieldCell, on the other + // hand setShowsFirstResponder causes a hangs when set on NSComboBoxCell + NSRect out = [pBtn focusRingMaskBoundsForFrame: rect inView: pView]; + CGContextFillRect(context, out); + } + else if ([pBtn isKindOfClass: [NSSliderCell class]]) + { + // Not getting anything useful for a NSSliderCell, so use the knob + [static_cast<NSSliderCell*>(pBtn) drawKnob: rect]; + } + else + [pBtn drawFocusRingMaskWithFrame:rect inView: pView]; + + CGContextEndTransparencyLayer(context); + } + + [NSGraphicsContext setCurrentContext:savedContext]; + CGContextRestoreGState(context); +} + +static void paintFocusRect(double radius, const NSRect& rect, CGContextRef context) +{ + NSRect bounds = rect; + + CGPathRef path = CGPathCreateWithRoundedRect(bounds, radius, radius, nullptr); + CGContextSetStrokeColorWithColor(context, [NSColor keyboardFocusIndicatorColor].CGColor); + CGContextSetLineWidth(context, FOCUS_RING_WIDTH); + CGContextBeginPath(context); + CGContextAddPath(context, path); + CGContextStrokePath(context); + CFRelease(path); +} + +@interface FixedWidthTabViewItem : NSTabViewItem { + int m_nWidth; +} +- (NSSize)sizeOfLabel: (BOOL)computeMin; +- (void)setTabWidth: (int)nWidth; +@end + +@implementation FixedWidthTabViewItem +- (NSSize)sizeOfLabel: (BOOL)computeMin +{ + NSSize size = [super sizeOfLabel: computeMin]; + size.width = m_nWidth; + return size; +} +- (void)setTabWidth: (int)nWidth +{ + m_nWidth = nWidth; +} +@end + +bool AquaGraphicsBackend::drawNativeControl(ControlType nType, + ControlPart nPart, + const tools::Rectangle &rControlRegion, + ControlState nState, + const ImplControlValue &aValue) +{ + if (!mrShared.checkContext()) + return false; + mrShared.maContextHolder.saveState(); + bool bOK = performDrawNativeControl(nType, nPart, rControlRegion, nState, aValue, + mrShared.maContextHolder.get(), mrShared.mpFrame); + mrShared.maContextHolder.restoreState(); + + tools::Rectangle buttonRect = rControlRegion; + + // in most cases invalidating the whole control region instead of just the unclipped part of it is sufficient (and probably + // faster). However for the window background we should not unnecessarily enlarge the really changed rectangle since the + // difference is usually quite high. Background is always drawn as a whole since we don't know anything about its possible + // contents (see issue i90291). + + if (nType == ControlType::WindowBackground) + { + CGRect aRect = {{0, 0}, {0, 0}}; + if (mrShared.mxClipPath) + aRect = CGPathGetBoundingBox(mrShared.mxClipPath); + if (aRect.size.width != 0 && aRect.size.height != 0) + buttonRect.Intersection(tools::Rectangle(Point(static_cast<tools::Long>(aRect.origin.x), + static_cast<tools::Long>(aRect.origin.y)), + Size(static_cast<tools::Long>(aRect.size.width), + static_cast<tools::Long>(aRect.size.height)))); + } + mrShared.refreshRect(buttonRect.Left(), buttonRect.Top(), buttonRect.GetWidth(), buttonRect.GetHeight()); + return bOK; +} + +static void drawBox(CGContextRef context, const NSRect& rc, NSColor* pColor) +{ + CGContextSaveGState(context); + CGContextTranslateCTM(context, rc.origin.x, rc.origin.y + rc.size.height); + CGContextScaleCTM(context, 1, -1); + + NSGraphicsContext* graphicsContext = [NSGraphicsContext graphicsContextWithCGContext:context flipped:NO]; + + NSRect rect = { NSZeroPoint, NSMakeSize(rc.size.width, rc.size.height) }; + NSBox* pBox = [[NSBox alloc] initWithFrame: rect]; + + [pBox setBoxType: NSBoxCustom]; + [pBox setFillColor: pColor]; + SAL_WNODEPRECATED_DECLARATIONS_PUSH // setBorderType first deprecated in macOS 10.15 + [pBox setBorderType: NSNoBorder]; + SAL_WNODEPRECATED_DECLARATIONS_POP + + [pBox displayRectIgnoringOpacity: rect inContext: graphicsContext]; + + [pBox release]; + + CGContextRestoreGState(context); +} + +// if I don't crystallize this bg then the InvertCursor using kCGBlendModeDifference doesn't +// work correctly and the cursor doesn't appear correctly +static void drawEditableBackground(CGContextRef context, const NSRect& rc) +{ + CGContextSaveGState(context); + CGContextSetFillColorWithColor(context, [NSColor controlBackgroundColor].CGColor); + CGContextFillRect(context, rc); + CGContextRestoreGState(context); +} + +// As seen in macOS 12.3.1. All a bit odd really. +const int RoundedMargin[4] = { 6, 4, 0, 3 }; + +bool AquaGraphicsBackendBase::performDrawNativeControl(ControlType nType, + ControlPart nPart, + const tools::Rectangle &rControlRegion, + ControlState nState, + const ImplControlValue &aValue, + CGContextRef context, + AquaSalFrame* mpFrame) +{ + bool bOK = false; + AquaSalInstance* pInst = GetSalData()->mpInstance; + HIRect rc = ImplGetHIRectFromRectangle(rControlRegion); + switch (nType) + { + case ControlType::Toolbar: + { + drawBox(context, rc, NSColor.windowBackgroundColor); + bOK = true; + } + break; + case ControlType::WindowBackground: + { + drawBox(context, rc, NSColor.windowBackgroundColor); + bOK = true; + } + break; + case ControlType::Tooltip: + { + rc.size.width += 2; + rc.size.height += 2; + drawBox(context, rc, NSColor.controlBackgroundColor); + bOK = true; + } + break; + case ControlType::Menubar: + case ControlType::MenuPopup: + if (nPart == ControlPart::Entire || nPart == ControlPart::MenuItem || nPart == ControlPart::HasBackgroundTexture) + { + // FIXME: without this magical offset there is a 2 pixel black border on the right + + rc.size.width += 2; + HIThemeMenuDrawInfo aMenuInfo; + aMenuInfo.version = 0; + aMenuInfo.menuType = kThemeMenuTypePullDown; + HIThemeMenuItemDrawInfo aMenuItemDrawInfo; + + // grey theme when the item is selected is drawn here. + + aMenuItemDrawInfo.itemType = kThemeMenuItemPlain; + if ((nPart == ControlPart::MenuItem) && (nState & ControlState::SELECTED)) + + // blue theme when the item is selected is drawn here. + + aMenuItemDrawInfo.state = kThemeMenuSelected; + else + + // normal color for non selected item + + aMenuItemDrawInfo.state = kThemeMenuActive; + + // repaints the background of the pull down menu + + HIThemeDrawMenuBackground(&rc, &aMenuInfo, context, kHIThemeOrientationNormal); + + // repaints the item either blue (selected) and/or grey (active only) + + HIThemeDrawMenuItem(&rc, &rc, &aMenuItemDrawInfo, context, kHIThemeOrientationNormal, &rc); + bOK = true; + } + else if (nPart == ControlPart::MenuItemCheckMark || nPart == ControlPart::MenuItemRadioMark) + { + // checked, else it is not displayed (see vcl/source/window/menu.cxx) + + if (nState & ControlState::PRESSED) + { + HIThemeTextInfo aTextInfo; + aTextInfo.version = 0; + aTextInfo.state = (nState & ControlState::ENABLED) ? kThemeStateInactive: kThemeStateActive; + aTextInfo.fontID = kThemeMenuItemMarkFont; + aTextInfo.horizontalFlushness = kHIThemeTextHorizontalFlushCenter; + aTextInfo.verticalFlushness = kHIThemeTextVerticalFlushTop; + aTextInfo.options = kHIThemeTextBoxOptionNone; + aTextInfo.truncationPosition = kHIThemeTextTruncationNone; + + // aTextInfo.truncationMaxLines unused because of kHIThemeTextTruncationNone item highlighted + + if (nState & ControlState::SELECTED) aTextInfo.state = kThemeStatePressed; + UniChar mark=(nPart == ControlPart::MenuItemCheckMark) ? kCheckUnicode: kBulletUnicode; + CFStringRef cfString = CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, &mark, 1, kCFAllocatorNull); + HIThemeDrawTextBox(cfString, &rc, &aTextInfo, context, kHIThemeOrientationNormal); + if (cfString) + CFRelease(cfString); + bOK = true; + } + } + break; + case ControlType::Pushbutton: + { + NSControlSize eSizeKind = NSControlSizeRegular; + NSBezelStyle eBezelStyle = NSBezelStyleRounded; + + PushButtonValue const *pPBVal = aValue.getType() == ControlType::Pushbutton ? + static_cast<PushButtonValue const *>(&aValue) : nullptr; + + SInt32 nPaintHeight = rc.size.height; + if (rc.size.height <= PUSH_BUTTON_NORMAL_HEIGHT) + { + eSizeKind = NSControlSizeMini; + GetThemeMetric(kThemeMetricSmallPushButtonHeight, &nPaintHeight); + } + else if ((pPBVal && pPBVal->mbSingleLine) || rc.size.height < PUSH_BUTTON_NORMAL_HEIGHT * 3 / 2) + { + GetThemeMetric(kThemeMetricPushButtonHeight, &nPaintHeight); + } + else + { + // A simple square bezel style that can scale to any size + eBezelStyle = NSBezelStyleSmallSquare; + } + + // translate the origin for controls with fixed paint height so content ends up somewhere sensible + rc.origin.y += (rc.size.height - nPaintHeight + 1) / 2; + rc.size.height = nPaintHeight; + + NSButtonCell* pBtn = pInst->mpButtonCell; + pBtn.allowsMixedState = YES; + + [pBtn setTitle: @""]; + [pBtn setButtonType: NSButtonTypeMomentaryPushIn]; + [pBtn setBezelStyle: eBezelStyle]; + [pBtn setState: ImplGetButtonValue(aValue.getTristateVal())]; + [pBtn setEnabled: getEnabled(nState, mpFrame)]; + [pBtn setFocusRingType: NSFocusRingTypeExterior]; + [pBtn setHighlighted: (nState & ControlState::PRESSED) ? YES : NO]; + [pBtn setControlSize: eSizeKind]; + if (nState & ControlState::DEFAULT) + [pBtn setKeyEquivalent: @"\r"]; + else + [pBtn setKeyEquivalent: @""]; + + if (eBezelStyle == NSBezelStyleRounded) + { + int nMargin = RoundedMargin[eSizeKind]; + rc.origin.x -= nMargin; + rc.size.width += nMargin * 2; + + rc.origin.x += FOCUS_RING_WIDTH / 2; + rc.size.width -= FOCUS_RING_WIDTH; + } + + const bool bFocused(nState & ControlState::FOCUSED); + paintCell(pBtn, rc, bFocused, context, nullptr); + + bOK = true; + } + break; + case ControlType::Radiobutton: + case ControlType::Checkbox: + { + rc.size.width -= 2 * FOCUS_RING_WIDTH; + rc.size.height = RADIO_BUTTON_SMALL_SIZE; + rc.origin.x += FOCUS_RING_WIDTH; + rc.origin.y += FOCUS_RING_WIDTH; + + NSButtonCell* pBtn = nType == ControlType::Checkbox ? pInst->mpCheckCell : pInst->mpRadioCell; + pBtn.allowsMixedState = YES; + + [pBtn setTitle: @""]; + [pBtn setButtonType: nType == ControlType::Checkbox ? NSButtonTypeSwitch : NSButtonTypeRadio]; + [pBtn setState: ImplGetButtonValue(aValue.getTristateVal())]; + [pBtn setEnabled: getEnabled(nState, mpFrame)]; + [pBtn setFocusRingType: NSFocusRingTypeExterior]; + [pBtn setHighlighted: (nState & ControlState::PRESSED) ? YES : NO]; + + const bool bFocused(nState & ControlState::FOCUSED); + paintCell(pBtn, rc, bFocused, context, nullptr); + + bOK = true; + } + break; + case ControlType::ListNode: + { + NSButtonCell* pBtn = pInst->mpListNodeCell; + pBtn.allowsMixedState = YES; + + [pBtn setTitle: @""]; + [pBtn setButtonType: NSButtonTypeOnOff]; + [pBtn setBezelStyle: NSBezelStyleDisclosure]; + [pBtn setState: ImplGetButtonValue(aValue.getTristateVal())]; + [pBtn setEnabled: getEnabled(nState, mpFrame)]; + [pBtn setFocusRingType: NSFocusRingTypeExterior]; + + const bool bFocused(nState & ControlState::FOCUSED); + paintCell(pBtn, rc, bFocused, context, nullptr); + + bOK = true; + } + break; + case ControlType::LevelBar: + { + NSRect rect = { NSZeroPoint, NSMakeSize(rc.size.width, rc.size.height) }; + NSLevelIndicator* pBox = [[NSLevelIndicator alloc] initWithFrame:rect]; + [pBox setLevelIndicatorStyle: NSLevelIndicatorStyleContinuousCapacity]; + [pBox setMinValue: 0]; + [pBox setMaxValue: rc.size.width]; + [pBox setCriticalValue: rc.size.width * 35.0 / 100.0]; + [pBox setWarningValue: rc.size.width * 70.0 / 100.0]; + [pBox setDoubleValue: aValue.getNumericVal()]; + + CGContextSaveGState(context); + CGContextTranslateCTM(context, rc.origin.x, rc.origin.y); + + NSGraphicsContext* savedContext = [NSGraphicsContext currentContext]; + NSGraphicsContext* graphicsContext = [NSGraphicsContext graphicsContextWithCGContext:context flipped:NO]; + [NSGraphicsContext setCurrentContext: graphicsContext]; + + [pBox drawRect: rect]; + + [NSGraphicsContext setCurrentContext: savedContext]; + + CGContextRestoreGState(context); + + [pBox release]; + + bOK = true; + } + break; + case ControlType::Progress: + case ControlType::IntroProgress: + { + NSRect rect = { NSZeroPoint, NSMakeSize(rc.size.width, rc.size.height) }; + NSProgressIndicator* pBox = [[NSProgressIndicator alloc] initWithFrame: rect]; + [pBox setControlSize: (rc.size.height > MEDIUM_PROGRESS_INDICATOR_HEIGHT) ? + NSControlSizeRegular : NSControlSizeSmall]; + [pBox setMinValue: 0]; + [pBox setMaxValue: rc.size.width]; + [pBox setDoubleValue: aValue.getNumericVal()]; + pBox.usesThreadedAnimation = NO; + [pBox setIndeterminate: NO]; + + CGContextSaveGState(context); + CGContextTranslateCTM(context, rc.origin.x, rc.origin.y); + + NSGraphicsContext* savedContext = [NSGraphicsContext currentContext]; + NSGraphicsContext* graphicsContext = [NSGraphicsContext graphicsContextWithCGContext:context flipped:NO]; + [NSGraphicsContext setCurrentContext: graphicsContext]; + + [pBox drawRect: rect]; + + [NSGraphicsContext setCurrentContext: savedContext]; + + CGContextRestoreGState(context); + + [pBox release]; + + bOK = true; + } + break; + case ControlType::Slider: + { + const SliderValue *pSliderVal = static_cast<SliderValue const *>(&aValue); + if (nPart == ControlPart::TrackHorzArea || nPart == ControlPart::TrackVertArea) + { + NSRect rect = { NSZeroPoint, NSMakeSize(rc.size.width, rc.size.height) }; + NSSlider* pBox = [[NSSlider alloc] initWithFrame: rect]; + + [pBox setEnabled: getEnabled(nState, mpFrame)]; + [pBox setVertical: nPart == ControlPart::TrackVertArea]; + [pBox setMinValue: pSliderVal->mnMin]; + [pBox setMaxValue: pSliderVal->mnMax]; + [pBox setIntegerValue: pSliderVal->mnCur]; + [pBox setSliderType: NSSliderTypeLinear]; + [pBox setFocusRingType: NSFocusRingTypeExterior]; + + const bool bFocused(nState & ControlState::FOCUSED); + paintCell(pBox.cell, rc, bFocused, context, mpFrame->getNSView()); + + [pBox release]; + + bOK = true; + } + } + break; + case ControlType::Scrollbar: + { + const ScrollbarValue *pScrollbarVal = (aValue.getType() == ControlType::Scrollbar) + ? static_cast<const ScrollbarValue *>(&aValue) : nullptr; + if (nPart == ControlPart::DrawBackgroundVert || nPart == ControlPart::DrawBackgroundHorz) + { + drawBox(context, rc, NSColor.controlBackgroundColor); + + NSRect rect = { NSZeroPoint, NSMakeSize(rc.size.width, rc.size.height) }; + NSScroller* pBar = [[NSScroller alloc] initWithFrame: rect]; + + double range = pScrollbarVal->mnMax - pScrollbarVal->mnVisibleSize - pScrollbarVal->mnMin; + double value = range ? (pScrollbarVal->mnCur - pScrollbarVal->mnMin) / range : 0; + + double length = pScrollbarVal->mnMax - pScrollbarVal->mnMin; + double proportion = pScrollbarVal->mnVisibleSize / length; + + [pBar setEnabled: getEnabled(nState, mpFrame)]; + [pBar setScrollerStyle: NSScrollerStyleLegacy]; + [pBar setFloatValue: value]; + [pBar setKnobProportion: proportion]; + bool bPressed = (pScrollbarVal->mnThumbState & ControlState::ENABLED) && + (pScrollbarVal->mnThumbState & ControlState::PRESSED); + + CGContextSaveGState(context); + CGContextTranslateCTM(context, rc.origin.x, rc.origin.y); + + NSGraphicsContext* graphicsContext = [NSGraphicsContext graphicsContextWithCGContext:context flipped:NO]; + + NSGraphicsContext* savedContext = [NSGraphicsContext currentContext]; + [NSGraphicsContext setCurrentContext: graphicsContext]; + + // For not-pressed first draw without the knob and then + // draw just the knob but with 50% opaque which looks sort of + // right + + [pBar drawKnobSlotInRect: rect highlight: NO]; + + NSBitmapImageRep* pImageRep = [pBar bitmapImageRepForCachingDisplayInRect: rect]; + + NSGraphicsContext* imageContext = [NSGraphicsContext graphicsContextWithBitmapImageRep:pImageRep]; + [NSGraphicsContext setCurrentContext: imageContext]; + + [pBar drawKnob]; + + [NSGraphicsContext setCurrentContext: graphicsContext]; + + NSImage* pImage = [[NSImage alloc] initWithSize: rect.size]; + [pImage addRepresentation: pImageRep]; // takes ownership of pImageRep + + [pImage drawInRect: rect fromRect: rect + operation: NSCompositingOperationSourceOver + fraction: bPressed ? 1.0 : 0.5]; + + [pImage release]; + + [NSGraphicsContext setCurrentContext:savedContext]; + + CGContextRestoreGState(context); + + bOK = true; + + [pBar release]; + } + } + break; + case ControlType::TabPane: + { + NSTabView* pBox = [[NSTabView alloc] initWithFrame: rc]; + + SInt32 nOverlap; + GetThemeMetric(kThemeMetricTabFrameOverlap, &nOverlap); + + // this calculation is probably more than a little dubious + rc.origin.x -= pBox.contentRect.origin.x - FOCUS_RING_WIDTH; + rc.size.width += rc.size.width - pBox.contentRect.size.width - 2 * FOCUS_RING_WIDTH; + double nTopBorder = pBox.contentRect.origin.y; + double nBottomBorder = rc.size.height - pBox.contentRect.size.height - nTopBorder; + double nExtraTop = (nTopBorder - nBottomBorder) / 2; + rc.origin.y -= (nTopBorder - nExtraTop + nOverlap); + rc.size.height += (nTopBorder - nExtraTop + nBottomBorder); + + CGContextSaveGState(context); + CGContextTranslateCTM(context, rc.origin.x, rc.origin.y); + + rc.origin.x = 0; + rc.origin.y = 0; + + [pBox setBoundsOrigin: rc.origin]; + [pBox setBoundsSize: rc.size]; + + // jam this in to force the tab contents area to be left undrawn, the ControlType::TabItem + // will be drawn in this space. + const TabPaneValue& rValue = static_cast<const TabPaneValue&>(aValue); + SInt32 nEndCapWidth; + GetThemeMetric(kThemeMetricLargeTabCapsWidth, &nEndCapWidth); + FixedWidthTabViewItem* pItem = [[[FixedWidthTabViewItem alloc] initWithIdentifier: @"tab"] autorelease]; + [pItem setTabWidth: rValue.m_aTabHeaderRect.GetWidth() - 2 * nEndCapWidth]; + [pBox addTabViewItem: pItem]; + + NSGraphicsContext* graphicsContext = [NSGraphicsContext graphicsContextWithCGContext:context flipped:NO]; + + NSGraphicsContext* savedContext = [NSGraphicsContext currentContext]; + [NSGraphicsContext setCurrentContext: graphicsContext]; + + [pBox drawRect: rc]; + + [NSGraphicsContext setCurrentContext: savedContext]; + + [pBox release]; + + CGContextRestoreGState(context); + + bOK = true; + } + break; + case ControlType::TabItem: + { + // first, last or middle tab + + TabitemValue const * pTabValue = static_cast<TabitemValue const *>(&aValue); + TabitemFlags nAlignment = pTabValue->mnAlignment; + + // TabitemFlags::LeftAligned (and TabitemFlags::RightAligned) for the leftmost (or rightmost) tab + // when there are several lines of tabs because there is only one first tab and one + // last tab and TabitemFlags::FirstInGroup (and TabitemFlags::LastInGroup) because when the + // line width is different from window width, there may not be TabitemFlags::RightAligned + int nPaintIndex = 1; + bool bSolo = false; + if (((nAlignment & TabitemFlags::LeftAligned) && (nAlignment & TabitemFlags::RightAligned)) + || ((nAlignment & TabitemFlags::FirstInGroup) && (nAlignment & TabitemFlags::LastInGroup))) + { + nPaintIndex = 0; + bSolo = true; + } + else if ((nAlignment & TabitemFlags::LeftAligned) || (nAlignment & TabitemFlags::FirstInGroup)) + nPaintIndex = !AllSettings::GetLayoutRTL() ? 0 : 2; + else if ((nAlignment & TabitemFlags::RightAligned) || (nAlignment & TabitemFlags::LastInGroup)) + nPaintIndex = !AllSettings::GetLayoutRTL() ? 2 : 0; + + int nCells = !bSolo ? 3 : 1; + NSRect ctrlrect = { NSZeroPoint, NSMakeSize(rc.size.width * nCells + FOCUS_RING_WIDTH, rc.size.height) }; + NSSegmentedControl* pCtrl = [[NSSegmentedControl alloc] initWithFrame: ctrlrect]; + [pCtrl setSegmentCount: nCells]; + if (bSolo) + [pCtrl setWidth: rc.size.width + FOCUS_RING_WIDTH forSegment: 0]; + else + { + [pCtrl setWidth: rc.size.width + FOCUS_RING_WIDTH/2 forSegment: 0]; + [pCtrl setWidth: rc.size.width forSegment: 1]; + [pCtrl setWidth: rc.size.width + FOCUS_RING_WIDTH/2 forSegment: 2]; + } + [pCtrl setSelected: (nState & ControlState::SELECTED) ? YES : NO forSegment: nPaintIndex]; + [pCtrl setFocusRingType: NSFocusRingTypeExterior]; + + NSGraphicsContext* savedContext = [NSGraphicsContext currentContext]; + [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithCGContext:context flipped:NO]]; + + NSRect rect = { NSZeroPoint, NSMakeSize(rc.size.width, rc.size.height) }; + NSRect tabrect = { NSMakePoint(rc.size.width * nPaintIndex + FOCUS_RING_WIDTH / 2, 0), + NSMakeSize(rc.size.width, rc.size.height) }; + NSBitmapImageRep* pImageRep = [pCtrl bitmapImageRepForCachingDisplayInRect: tabrect]; + [pCtrl cacheDisplayInRect: tabrect toBitmapImageRep: pImageRep]; + + NSImage* pImage = [[NSImage alloc] initWithSize: rect.size]; + [pImage addRepresentation: pImageRep]; // takes ownership of pImageRep + + [pImage drawInRect: rc fromRect: rect + operation: NSCompositingOperationSourceOver + fraction: 1.0]; + + [pImage release]; + + [NSGraphicsContext setCurrentContext:savedContext]; + + [pCtrl release]; + + if (nState & ControlState::FOCUSED) + { + if (!bSolo) + { + if (nPaintIndex == 0) + { + rc.origin.x += FOCUS_RING_WIDTH / 2; + rc.size.width -= FOCUS_RING_WIDTH / 2; + } + else if (nPaintIndex == 2) + { + rc.size.width -= FOCUS_RING_WIDTH / 2; + rc.size.width -= FOCUS_RING_WIDTH / 2; + } + } + + paintFocusRect(4.0, rc, context); + } + bOK=true; + } + break; + case ControlType::Editbox: + case ControlType::MultilineEditbox: + { + rc.size.width += 2 * EDITBOX_INSET_MARGIN; + if (nType == ControlType::Editbox) + rc.size.height = EDITBOX_HEIGHT; + else + rc.size.height += 2 * (EDITBOX_BORDER_WIDTH + EDITBOX_INSET_MARGIN); + rc.origin.x -= EDITBOX_INSET_MARGIN; + rc.origin.y -= EDITBOX_INSET_MARGIN; + + NSTextFieldCell* pBtn = pInst->mpTextFieldCell; + + [pBtn setEnabled: getEnabled(nState, mpFrame)]; + [pBtn setBezeled: YES]; + [pBtn setEditable: YES]; + [pBtn setFocusRingType: NSFocusRingTypeExterior]; + + drawEditableBackground(context, rc); + const bool bFocused(nState & ControlState::FOCUSED); + paintCell(pBtn, rc, bFocused, context, mpFrame->getNSView()); + + bOK = true; + } + break; + case ControlType::Combobox: + if (nPart == ControlPart::HasBackgroundTexture || nPart == ControlPart::Entire) + { + rc.origin.y += (rc.size.height - COMBOBOX_HEIGHT + 1) / 2; + rc.size.height = COMBOBOX_HEIGHT; + + NSComboBoxCell* pBtn = pInst->mpComboBoxCell; + + [pBtn setEnabled: getEnabled(nState, mpFrame)]; + [pBtn setEditable: YES]; + [pBtn setState: ImplGetButtonValue(aValue.getTristateVal())]; + [pBtn setFocusRingType: NSFocusRingTypeExterior]; + + { + rc.origin.x += 2; + rc.size.width -= 1; + } + + drawEditableBackground(context, rc); + const bool bFocused(nState & ControlState::FOCUSED); + paintCell(pBtn, rc, bFocused, context, mpFrame->getNSView()); + + bOK = true; + } + break; + case ControlType::Listbox: + + switch (nPart) + { + case ControlPart::Entire: + case ControlPart::ButtonDown: + { + rc.origin.y += (rc.size.height - LISTBOX_HEIGHT + 1) / 2; + rc.size.height = LISTBOX_HEIGHT; + + NSPopUpButtonCell* pBtn = pInst->mpPopUpButtonCell; + + [pBtn setTitle: @""]; + [pBtn setEnabled: getEnabled(nState, mpFrame)]; + [pBtn setFocusRingType: NSFocusRingTypeExterior]; + [pBtn setHighlighted: (nState & ControlState::PRESSED) ? YES : NO]; + if (nState & ControlState::DEFAULT) + [pBtn setKeyEquivalent: @"\r"]; + else + [pBtn setKeyEquivalent: @""]; + + { + rc.size.width += 1; + } + + const bool bFocused(nState & ControlState::FOCUSED); + paintCell(pBtn, rc, bFocused, context, nullptr); + + bOK = true; + break; + } + case ControlPart::ListboxWindow: + { + NSRect rect = { NSZeroPoint, NSMakeSize(rc.size.width, rc.size.height) }; + NSScrollView* pBox = [[NSScrollView alloc] initWithFrame: rect]; + [pBox setBorderType: NSLineBorder]; + + CGContextSaveGState(context); + CGContextTranslateCTM(context, rc.origin.x, rc.origin.y); + + NSGraphicsContext* savedContext = [NSGraphicsContext currentContext]; + NSGraphicsContext* graphicsContext = [NSGraphicsContext graphicsContextWithCGContext:context flipped:NO]; + [NSGraphicsContext setCurrentContext: graphicsContext]; + + [pBox drawRect: rect]; + + [NSGraphicsContext setCurrentContext: savedContext]; + + CGContextRestoreGState(context); + + [pBox release]; + + bOK = true; + break; + } + default: + break; + } + break; + case ControlType::Spinbox: + if (nPart == ControlPart::Entire) + { + // text field + + rc.size.width -= SPIN_BUTTON_WIDTH + 4 * FOCUS_RING_WIDTH; + rc.size.height = EDITBOX_HEIGHT; + rc.origin.x += FOCUS_RING_WIDTH; + rc.origin.y += FOCUS_RING_WIDTH; + + NSTextFieldCell* pEdit = pInst->mpTextFieldCell; + + [pEdit setEnabled: YES]; + [pEdit setBezeled: YES]; + [pEdit setEditable: YES]; + [pEdit setFocusRingType: NSFocusRingTypeExterior]; + + drawEditableBackground(context, rc); + const bool bFocused(nState & ControlState::FOCUSED); + paintCell(pEdit, rc, bFocused, context, mpFrame->getNSView()); + + // buttons + + const SpinbuttonValue *pSpinButtonVal = (aValue.getType() == ControlType::SpinButtons) + ? static_cast <const SpinbuttonValue *>(&aValue) : nullptr; + if (pSpinButtonVal) + { + ControlState nUpperState = pSpinButtonVal->mnUpperState; + ControlState nLowerState = pSpinButtonVal->mnLowerState; + + rc.origin.x += rc.size.width + FOCUS_RING_WIDTH + 1; + rc.origin.y -= 1; + rc.size.width = SPIN_BUTTON_WIDTH; + rc.size.height = SPIN_LOWER_BUTTON_HEIGHT + SPIN_LOWER_BUTTON_HEIGHT; + + NSStepperCell* pBtn = pInst->mpStepperCell; + + [pBtn setTitle: @""]; + [pBtn setState: ImplGetButtonValue(aValue.getTristateVal())]; + [pBtn setEnabled: (nUpperState & ControlState::ENABLED || nLowerState & ControlState::ENABLED) ? + YES : NO]; + [pBtn setFocusRingType: NSFocusRingTypeExterior]; + [pBtn setHighlighted: (nState & ControlState::PRESSED) ? YES : NO]; + + const bool bSpinFocused(nUpperState & ControlState::FOCUSED || nLowerState & ControlState::FOCUSED); + paintCell(pBtn, rc, bSpinFocused, context, nullptr); + } + bOK = true; + } + break; + case ControlType::Frame: + { + DrawFrameFlags nStyle = static_cast<DrawFrameFlags>(aValue.getNumericVal()); + if (nPart == ControlPart::Border) + { + if (!(nStyle & DrawFrameFlags::Menu) && !(nStyle & DrawFrameFlags::WindowBorder)) + { + + // strange effects start to happen when HIThemeDrawFrame meets the border of the window. + // These can be avoided by clipping to the boundary of the frame (see issue 84756) + + if (rc.origin.y + rc.size.height >= mpFrame->maGeometry.height() - 3) + { + CGMutablePathRef rPath = CGPathCreateMutable(); + CGPathAddRect(rPath, nullptr, + CGRectMake(0, 0, mpFrame->maGeometry.width() - 1, mpFrame->maGeometry.height() - 1)); + CGContextBeginPath(context); + CGContextAddPath(context, rPath); + CGContextClip(context); + CGPathRelease(rPath); + } + HIThemeFrameDrawInfo aTextDrawInfo; + aTextDrawInfo.version = 0; + aTextDrawInfo.kind = kHIThemeFrameListBox; + aTextDrawInfo.state = kThemeStateActive; + aTextDrawInfo.isFocused = false; + HIThemeDrawFrame(&rc, &aTextDrawInfo, context, kHIThemeOrientationNormal); + bOK = true; + } + } + } + break; + case ControlType::ListNet: + + // do nothing as there isn't net for listviews on macOS + + bOK = true; + break; + default: + break; + } + + return bOK; +} + +bool AquaSalGraphics::getNativeControlRegion(ControlType nType, + ControlPart nPart, + const tools::Rectangle &rControlRegion, + ControlState, + const ImplControlValue &aValue, + const OUString &, + tools::Rectangle &rNativeBoundingRegion, + tools::Rectangle &rNativeContentRegion) +{ + bool toReturn = false; + tools::Rectangle aCtrlBoundRect(rControlRegion); + short x = aCtrlBoundRect.Left(); + short y = aCtrlBoundRect.Top(); + short w, h; + switch (nType) + { + case ControlType::Pushbutton: + case ControlType::Radiobutton: + case ControlType::Checkbox: + { + if (nType == ControlType::Pushbutton) + { + w = aCtrlBoundRect.GetWidth(); + h = aCtrlBoundRect.GetHeight(); + } + else + { + w = RADIO_BUTTON_SMALL_SIZE + 2 * FOCUS_RING_WIDTH + RADIO_BUTTON_TEXT_SEPARATOR; + h = RADIO_BUTTON_SMALL_SIZE + 2 * FOCUS_RING_WIDTH; + } + rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h)); + rNativeBoundingRegion = tools::Rectangle(Point(x, y), Size(w, h)); + toReturn = true; + } + break; + case ControlType::LevelBar: + case ControlType::Progress: + { + tools::Rectangle aRect(aCtrlBoundRect); + if (aRect.GetHeight() < LARGE_PROGRESS_INDICATOR_HEIGHT) + aRect.SetBottom(aRect.Top() + MEDIUM_PROGRESS_INDICATOR_HEIGHT - 1); + else + aRect.SetBottom(aRect.Top() + LARGE_PROGRESS_INDICATOR_HEIGHT - 1); + rNativeBoundingRegion = aRect; + rNativeContentRegion = aRect; + toReturn = true; + } + break; + case ControlType::IntroProgress: + { + tools::Rectangle aRect(aCtrlBoundRect); + aRect.SetBottom(aRect.Top() + MEDIUM_PROGRESS_INDICATOR_HEIGHT - 1); + rNativeBoundingRegion = aRect; + rNativeContentRegion = aRect; + toReturn = true; + } + break; + case ControlType::Slider: + if (nPart == ControlPart::ThumbHorz) + { + w = SLIDER_WIDTH; + h = aCtrlBoundRect.GetHeight(); + rNativeBoundingRegion = rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h)); + toReturn = true; + } + else if (nPart == ControlPart::ThumbVert) + { + w = aCtrlBoundRect.GetWidth(); + h = SLIDER_HEIGHT; + rNativeBoundingRegion = rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h)); + toReturn = true; + } + break; + case ControlType::Scrollbar: + { + tools::Rectangle aRect; + if (AquaGetScrollRect(nPart, aCtrlBoundRect, aRect)) + { + toReturn = true; + rNativeBoundingRegion = aRect; + rNativeContentRegion = aRect; + } + } + break; + case ControlType::TabItem: + { + w = aCtrlBoundRect.GetWidth() + 2 * TAB_TEXT_MARGIN; + h = TAB_HEIGHT + 2; + rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h)); + rNativeBoundingRegion = tools::Rectangle(Point(x, y), Size(w, h)); + toReturn = true; + } + break; + case ControlType::Editbox: + { + const tools::Long nBorderThickness = FOCUS_RING_WIDTH + EDITBOX_BORDER_WIDTH + EDITBOX_INSET_MARGIN; + // tdf#144241 don't return a negative width, expand the region to the min osx width + w = std::max(nBorderThickness * 2, aCtrlBoundRect.GetWidth()); + h = EDITBOX_HEIGHT + 2 * FOCUS_RING_WIDTH; + rNativeBoundingRegion = tools::Rectangle(Point(x, y), Size(w, h)); + w -= 2 * nBorderThickness; + h -= 2 * nBorderThickness; + x += nBorderThickness; + y += nBorderThickness; + rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h)); + toReturn = true; + } + break; + case ControlType::Combobox: + if (nPart == ControlPart::Entire) + { + w = aCtrlBoundRect.GetWidth(); + h = COMBOBOX_HEIGHT + 2 * FOCUS_RING_WIDTH; + rNativeBoundingRegion = tools::Rectangle(Point(x, y), Size(w, h)); + rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h)); + toReturn = true; + } + else if (nPart == ControlPart::ButtonDown) + { + w = COMBOBOX_BUTTON_WIDTH + FOCUS_RING_WIDTH; + h = COMBOBOX_HEIGHT + 2 * FOCUS_RING_WIDTH; + x += aCtrlBoundRect.GetWidth() - w; + rNativeBoundingRegion = tools::Rectangle(Point(x, y), Size(w, h)); + rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h)); + toReturn = true; + } + else if (nPart == ControlPart::SubEdit) + { + w = aCtrlBoundRect.GetWidth() - 2 * FOCUS_RING_WIDTH - COMBOBOX_BUTTON_WIDTH - COMBOBOX_BORDER_WIDTH + - 2 * COMBOBOX_TEXT_MARGIN; + h = COMBOBOX_HEIGHT - 2 * COMBOBOX_BORDER_WIDTH; + x += FOCUS_RING_WIDTH + COMBOBOX_BORDER_WIDTH + COMBOBOX_TEXT_MARGIN; + y += FOCUS_RING_WIDTH + COMBOBOX_BORDER_WIDTH; + rNativeBoundingRegion = tools::Rectangle(Point(x, y), Size(w, h)); + rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h)); + toReturn = true; + } + break; + case ControlType::Listbox: + if (nPart == ControlPart::Entire) + { + w = aCtrlBoundRect.GetWidth(); + h = LISTBOX_HEIGHT + 2 * FOCUS_RING_WIDTH; + rNativeBoundingRegion = tools::Rectangle(Point(x, y), Size(w, h)); + rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h)); + toReturn = true; + } + else if (nPart == ControlPart::ButtonDown) + { + w = LISTBOX_BUTTON_WIDTH + FOCUS_RING_WIDTH; + h = LISTBOX_HEIGHT + 2 * FOCUS_RING_WIDTH; + x += aCtrlBoundRect.GetWidth() - w; + rNativeBoundingRegion = tools::Rectangle(Point(x, y), Size(w, h)); + rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h)); + toReturn = true; + } + else if (nPart == ControlPart::SubEdit) + { + w = aCtrlBoundRect.GetWidth() - 2 * FOCUS_RING_WIDTH - LISTBOX_BUTTON_WIDTH - LISTBOX_BORDER_WIDTH + - 2 * LISTBOX_TEXT_MARGIN; + h = LISTBOX_HEIGHT - 2 * LISTBOX_BORDER_WIDTH; + x += FOCUS_RING_WIDTH + LISTBOX_BORDER_WIDTH + LISTBOX_TEXT_MARGIN; + y += FOCUS_RING_WIDTH + LISTBOX_BORDER_WIDTH; + rNativeBoundingRegion = tools::Rectangle(Point(x, y), Size(w, h)); + rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h)); + toReturn = true; + } + else if (nPart == ControlPart::ListboxWindow) + { + w = aCtrlBoundRect.GetWidth() - 2; + h = aCtrlBoundRect.GetHeight() - 2; + x += 1; + y += 1; + rNativeBoundingRegion = aCtrlBoundRect; + rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h)); + toReturn = true; + } + break; + case ControlType::Spinbox: + if (nPart == ControlPart::Entire) + { + w = aCtrlBoundRect.GetWidth(); + h = EDITBOX_HEIGHT + 2 * FOCUS_RING_WIDTH; + x += SPINBOX_OFFSET; + rNativeBoundingRegion = tools::Rectangle(Point(x, y), Size(w, h)); + rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h)); + toReturn = true; + } + else if (nPart == ControlPart::SubEdit) + { + w = aCtrlBoundRect.GetWidth() - 4 * FOCUS_RING_WIDTH - SPIN_BUTTON_WIDTH - 2 * EDITBOX_BORDER_WIDTH + - 2 * EDITBOX_INSET_MARGIN; + h = EDITBOX_HEIGHT - 2 * (EDITBOX_BORDER_WIDTH + EDITBOX_INSET_MARGIN); + x += FOCUS_RING_WIDTH + EDITBOX_BORDER_WIDTH + EDITBOX_INSET_MARGIN + SPINBOX_OFFSET; + y += FOCUS_RING_WIDTH + EDITBOX_BORDER_WIDTH + EDITBOX_INSET_MARGIN; + rNativeBoundingRegion = tools::Rectangle(Point(x, y), Size(w, h)); + rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h)); + toReturn = true; + } + else if (nPart == ControlPart::ButtonUp) + { + w = SPIN_BUTTON_WIDTH + 2 * FOCUS_RING_WIDTH; + h = SPIN_UPPER_BUTTON_HEIGHT + FOCUS_RING_WIDTH; + x += aCtrlBoundRect.GetWidth() - SPIN_BUTTON_WIDTH - 2 * FOCUS_RING_WIDTH + SPINBOX_OFFSET; + rNativeBoundingRegion = tools::Rectangle(Point(x, y), Size(w, h)); + rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h)); + toReturn = true; + } + else if (nPart == ControlPart::ButtonDown) + { + w = SPIN_BUTTON_WIDTH + 2 * FOCUS_RING_WIDTH; + h = SPIN_LOWER_BUTTON_HEIGHT + FOCUS_RING_WIDTH; + x += aCtrlBoundRect.GetWidth() - SPIN_BUTTON_WIDTH - 2 * FOCUS_RING_WIDTH + SPINBOX_OFFSET; + y += FOCUS_RING_WIDTH + SPIN_UPPER_BUTTON_HEIGHT; + rNativeBoundingRegion = tools::Rectangle(Point(x, y), Size(w, h)); + rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h)); + toReturn = true; + } + break; + case ControlType::Frame: + { + DrawFrameStyle nStyle = static_cast<DrawFrameStyle>(aValue.getNumericVal() & 0x000f); + DrawFrameFlags nFlags = static_cast<DrawFrameFlags>(aValue.getNumericVal() & 0xfff0); + if (nPart == ControlPart::Border + && !(nFlags & (DrawFrameFlags::Menu | DrawFrameFlags::WindowBorder | DrawFrameFlags::BorderWindowBorder))) + { + tools::Rectangle aRect(aCtrlBoundRect); + if (nStyle == DrawFrameStyle::DoubleIn) + { + aRect.AdjustLeft(1); + aRect.AdjustTop(1); + // rRect.Right() -= 1; + // rRect.Bottom() -= 1; + } + else + { + aRect.AdjustLeft(1); + aRect.AdjustTop(1); + aRect.AdjustRight(-1); + aRect.AdjustBottom(-1); + } + rNativeContentRegion = aRect; + rNativeBoundingRegion = aRect; + toReturn = true; + } + } + break; + case ControlType::Menubar: + case ControlType::MenuPopup: + if (nPart == ControlPart::MenuItemCheckMark || nPart == ControlPart::MenuItemRadioMark) + { + w=10; + h=10; + rNativeContentRegion = tools::Rectangle(Point(x, y), Size(w, h)); + rNativeBoundingRegion = tools::Rectangle(Point(x, y), Size(w, h)); + toReturn = true; + } + break; + default: + break; + } + return toReturn; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |