/* -*- 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 .
*/
// 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.
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;
}
}
staticbool 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) returntrue; break; case ControlType::Scrollbar: if (nPart == ControlPart::DrawBackgroundHorz || nPart == ControlPart::DrawBackgroundVert
|| nPart == ControlPart::Entire || nPart == ControlPart::HasThreeButtons) returntrue; break; case ControlType::Slider: if (nPart == ControlPart::TrackHorzArea || nPart == ControlPart::TrackVertArea) returntrue; break; case ControlType::Editbox: if (nPart == ControlPart::Entire || nPart == ControlPart::HasBackgroundTexture) returntrue; break; case ControlType::MultilineEditbox: if (nPart == ControlPart::Entire || nPart == ControlPart::HasBackgroundTexture) returntrue; break; case ControlType::Spinbox: if (nPart == ControlPart::Entire || nPart == ControlPart::AllButtons || nPart == ControlPart::HasBackgroundTexture) returntrue; break; case ControlType::SpinButtons: returnfalse; case ControlType::Combobox: if (nPart == ControlPart::Entire || nPart == ControlPart::HasBackgroundTexture) returntrue; break; case ControlType::Listbox: if (nPart == ControlPart::Entire || nPart == ControlPart::ListboxWindow || nPart == ControlPart::HasBackgroundTexture
|| nPart == ControlPart::SubEdit) returntrue; break; case ControlType::TabItem: case ControlType::TabPane: case ControlType::TabBody: if (nPart == ControlPart::Entire || nPart == ControlPart::TabsDrawRtl || nPart == ControlPart::HasBackgroundTexture) returntrue; break; case ControlType::Toolbar: if (nPart == ControlPart::Entire || nPart == ControlPart::DrawBackgroundHorz
|| nPart == ControlPart::DrawBackgroundVert) returntrue; break; case ControlType::WindowBackground: if (nPart == ControlPart::BackgroundWindow || nPart == ControlPart::BackgroundDialog) returntrue; break; case ControlType::Menubar: if (nPart == ControlPart::Entire) returntrue; break; case ControlType::Tooltip: if (nPart == ControlPart::Entire) returntrue; break; case ControlType::MenuPopup: if (nPart == ControlPart::Entire || nPart == ControlPart::MenuItem || nPart == ControlPart::MenuItemCheckMark
|| nPart == ControlPart::MenuItemRadioMark) returntrue; break; case ControlType::LevelBar: case ControlType::Progress: case ControlType::IntroProgress: if (nPart == ControlPart::Entire) returntrue; break; case ControlType::Frame: if (nPart == ControlPart::Border) returntrue; break; case ControlType::ListNet: if (nPart == ControlPart::Entire) returntrue; break; default: break;
} returnfalse;
}
// 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.
bool AquaSalGraphics::drawNativeControl(ControlType nType,
ControlPart nPart, const tools::Rectangle &rControlRegion,
ControlState nState, const ImplControlValue &aValue, const OUString &, const Color&)
{ // tdf#165266 Force native controls to use current effective appearance // +[NSAppearance setCurrentAppearance:] is deprecated and calling // that appears to do less and less with each new version of macos // and/or Xcode so run all drawing of native controls in a block passed // to -[NSAppearance performAsCurrentDrawingAppearance:].
__block bool bRet = false; if (@available(macOS 11, *))
{
[[NSApp effectiveAppearance] performAsCurrentDrawingAppearance:^() {
bRet = mpBackend->drawNativeControl(nType, nPart, rControlRegion, nState, aValue);
}];
} else
{
bRet = mpBackend->drawNativeControl(nType, nPart, rControlRegion, nState, aValue);
}
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 constbool 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);
} elseif ([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];
// 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).
// -[NSBox setBorderType: NSNoBorder] is deprecated so hide the border // by setting the border color to transparent
[pBox setBorderColor: [NSColor clearColor]];
[pBox setTitlePosition: NSNoTitle];
[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 staticvoid drawEditableBackground(CGContextRef context, const NSRect& rc)
{
CGContextSaveGState(context); if (ThemeColors::VclPluginCanUseThemeColors())
CGContextSetFillColorWithColor(context, colorFromRGB(ThemeColors::GetThemeColors().GetBaseColor()).CGColor); else
CGContextSetFillColorWithColor(context, [NSColor controlBackgroundColor].CGColor);
CGContextFillRect(context, rc);
CGContextRestoreGState(context);
}
// As seen in macOS 12.3.1. All a bit odd really. constint 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; bool bCanUseThemeColors(ThemeColors::VclPluginCanUseThemeColors());
AquaSalInstance* pInst = GetSalData()->mpInstance;
HIRect rc = ImplGetHIRectFromRectangle(rControlRegion); switch (nType)
{ case ControlType::Toolbar:
{ if (bCanUseThemeColors)
drawBox(context, rc, colorFromRGB(ThemeColors::GetThemeColors().GetWindowColor())); else
drawBox(context, rc, NSColor.windowBackgroundColor);
bOK = true;
} break; case ControlType::WindowBackground:
{ if (bCanUseThemeColors)
drawBox(context, rc, colorFromRGB(ThemeColors::GetThemeColors().GetWindowColor())); else
drawBox(context, rc, NSColor.windowBackgroundColor);
bOK = true;
} break; case ControlType::Tooltip:
{
rc.size.width += 2;
rc.size.height += 2; if (bCanUseThemeColors)
drawBox(context, rc, colorFromRGB(ThemeColors::GetThemeColors().GetBaseColor())); else
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
double range = pScrollbarVal->mnMax - pScrollbarVal->mnVisibleSize - pScrollbarVal->mnMin; double value = range ? (pScrollbarVal->mnCur - pScrollbarVal->mnMin) / range : 0;
// 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];
// 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;
} elseif ((nAlignment & TabitemFlags::LeftAligned) || (nAlignment & TabitemFlags::FirstInGroup))
nPaintIndex = !AllSettings::GetLayoutRTL() ? 0 : 2; elseif ((nAlignment & TabitemFlags::RightAligned) || (nAlignment & TabitemFlags::LastInGroup))
nPaintIndex = !AllSettings::GetLayoutRTL() ? 2 : 0;
constbool 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)
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.