mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-09-02 12:40:33 +02:00
Cocoa: improve the settings of FoucsRing to better adapt to LCL, Merge branch 'cocoa/focusring'
on macOS, the FocusRing takes up extra space, which may cause strange display in some cases. it may block other controls, or be partially cut off. for example, in the Lazarus IDE - About dialog, the FocusRing of the Tab of TPageControl is partially cut off. by providing a configurable infrastructure, FocusRing can be controlled for different control types.
This commit is contained in:
commit
fba4c8e459
@ -23,8 +23,8 @@ unit CocoaButtons;
|
||||
interface
|
||||
|
||||
uses
|
||||
Types, Classes, SysUtils, Graphics,
|
||||
MacOSAll, CocoaAll, CocoaConst, CocoaPrivate, CocoaCallback;
|
||||
Types, Classes, SysUtils, Graphics, Controls,
|
||||
MacOSAll, CocoaAll, CocoaConst, CocoaWSCommon, CocoaPrivate, CocoaCallback;
|
||||
|
||||
|
||||
const
|
||||
@ -92,6 +92,8 @@ type
|
||||
procedure setState(astate: NSInteger); override;
|
||||
end;
|
||||
|
||||
TCocoaButtonNeedFocusRing = objcclass(TCocoaButton)
|
||||
end;
|
||||
|
||||
IStepperCallback = interface(ICommonCallback)
|
||||
procedure BeforeChange(var Allowed: Boolean);
|
||||
@ -360,6 +362,7 @@ begin
|
||||
begin
|
||||
setTarget(Self);
|
||||
setAction(objcselector('actionButtonClick:'));
|
||||
UpdateControlFocusRing( self, TWinControl(lclGetTarget) );
|
||||
// todo: find a way to release notifications below
|
||||
// NSNotificationCenter.defaultCenter.addObserver_selector_name_object(Self, objcselector('boundsDidChange:'), NSViewBoundsDidChangeNotification, Result);
|
||||
// NSNotificationCenter.defaultCenter.addObserver_selector_name_object(Self, objcselector('frameDidChange:'), NSViewFrameDidChangeNotification, Result);
|
||||
|
@ -9,8 +9,31 @@ uses
|
||||
CocoaAll, Cocoa_Extra, CocoaConst;
|
||||
|
||||
type
|
||||
NSColorFunction = Function(): NSColor;
|
||||
// on macOS, the FocusRing takes up extra space, which may cause strange
|
||||
// display in some cases. it may block other controls, or be partially cut off.
|
||||
// for example, in the Lazarus IDE - About dialog, the FocusRing of the
|
||||
// Tab of TPageControl is partially cut off.
|
||||
// by providing a configurable infrastructure, FocusRing can be controlled
|
||||
// for different control types.
|
||||
{$scopedEnums on}
|
||||
TCocoaFocusRingStrategy = (
|
||||
default, // by macOS Default
|
||||
none, // no FoucsRing
|
||||
required, // have FocusRing
|
||||
border // by LCL Control Border
|
||||
);
|
||||
|
||||
// set the FocusRing strategy of the control according to ClassName
|
||||
// (eg. 'TCocoaTabControl')
|
||||
// APP can change the default setting of Cocoa WidgetSet
|
||||
// by calling setCocoaControlFocusRingStrategy() too.
|
||||
procedure setCocoaControlFocusRingStrategry( frs: TCocoaFocusRingStrategy; AClassName: NSString );
|
||||
|
||||
// getCocoaControlFocusRingStrategy() is mainly used internally by Cocoa WidgetSet
|
||||
function getCocoaControlFocusRingStrategry( AClassName: NSString ): TCocoaFocusRingStrategy;
|
||||
|
||||
type
|
||||
NSColorFunction = Function(): NSColor;
|
||||
function getCocoaScrollerDefaultKnobColor: NSColor;
|
||||
|
||||
var
|
||||
@ -129,6 +152,46 @@ var
|
||||
|
||||
implementation
|
||||
|
||||
var
|
||||
FocusRingStrategySetting: NSMutableDictionary;
|
||||
|
||||
procedure setCocoaControlFocusRingStrategry( frs: TCocoaFocusRingStrategy; AClassName: NSString );
|
||||
var
|
||||
valueObject: NSNumber;
|
||||
begin
|
||||
valueObject:= NSNumber.numberWithInt( Ord(frs) );
|
||||
FocusRingStrategySetting.setValue_forKey( valueObject , AClassName );
|
||||
end;
|
||||
|
||||
function getCocoaControlFocusRingStrategry( AClassName: NSString ): TCocoaFocusRingStrategy;
|
||||
var
|
||||
valueObject: NSNumber;
|
||||
begin
|
||||
Result:= TCocoaFocusRingStrategy.default;
|
||||
valueObject:= NSNumber( FocusRingStrategySetting.valueForKey(AClassName) );
|
||||
if Assigned(valueObject) then
|
||||
Result:= TCocoaFocusRingStrategy(valueObject.intValue);
|
||||
if Result = TCocoaFocusRingStrategy.required then
|
||||
Writeln( 'required' );
|
||||
end;
|
||||
|
||||
// no need to set TCocoaFocusRingStrategy.default control
|
||||
// the controls not in FocusRingStrategySetting are TCocoaFocusRingStrategy.default
|
||||
procedure initDefaultFoucsRingSetting;
|
||||
begin
|
||||
FocusRingStrategySetting:= NSMutableDictionary.alloc.initWithCapacity( 16 );
|
||||
|
||||
setCocoaControlFocusRingStrategry( TCocoaFocusRingStrategy.none, NSSTR('TCocoaTabControl') );
|
||||
setCocoaControlFocusRingStrategry( TCocoaFocusRingStrategy.none, NSSTR('TCocoaButton') );
|
||||
setCocoaControlFocusRingStrategry( TCocoaFocusRingStrategy.none, NSSTR('TCocoaTextField') );
|
||||
setCocoaControlFocusRingStrategry( TCocoaFocusRingStrategy.none, NSSTR('TCocoaComboBox') );
|
||||
setCocoaControlFocusRingStrategry( TCocoaFocusRingStrategy.none, NSSTR('TCocoaReadOnlyComboBox') );
|
||||
setCocoaControlFocusRingStrategry( TCocoaFocusRingStrategy.none, NSSTR('TCocoaTableListView') );
|
||||
setCocoaControlFocusRingStrategry( TCocoaFocusRingStrategy.none, NSSTR('TCocoaCollectionView') );
|
||||
|
||||
setCocoaControlFocusRingStrategry( TCocoaFocusRingStrategy.border, NSSTR('TCocoaTextView') );
|
||||
end;
|
||||
|
||||
function getCocoaScrollerDefaultKnobColor: NSColor;
|
||||
begin
|
||||
Result:= NSColor.controlTextColor;
|
||||
@ -137,6 +200,6 @@ end;
|
||||
initialization
|
||||
CocoaDefaultCheckMenuImageName:= NSSTR('NSMenuCheckmark');
|
||||
CocoaDefaultRadioMenuImageName:= NSSTR('NSDatePickerCalendarHome');
|
||||
|
||||
initDefaultFoucsRingSetting;
|
||||
end.
|
||||
|
||||
|
@ -194,6 +194,8 @@ begin
|
||||
_scrollView.callback:= self.callback;
|
||||
self.addSubview_positioned_relativeTo( _scrollView, NSWindowBelow, nil );
|
||||
ScrollViewSetBorderStyle( _scrollView, callback.getBorderStyle );
|
||||
_scrollView.setFocusRingType( NSFocusRingTypeExterior );
|
||||
UpdateControlFocusRing( _backendControl, TWinControl(self.lclGetTarget) );
|
||||
|
||||
backendControlAccess:= TCocoaListViewBackendControlProtocol(_backendControl);
|
||||
backendControlAccess.backend_setCallback( self.callback );
|
||||
@ -529,6 +531,7 @@ begin
|
||||
|
||||
field:= TCocoaTextField( aView );
|
||||
field.setBezeled( False );
|
||||
field.setFocusRingType( NSFocusRingTypeExterior );
|
||||
field.fixedBorderStyle:= True;
|
||||
NSView(self.Owner).addSubview( field ); // add to TCococListView
|
||||
Result:= True;
|
||||
|
@ -428,8 +428,6 @@ begin
|
||||
self.setAllowsColumnReordering(False);
|
||||
self.setAllowsColumnSelection(False);
|
||||
|
||||
UpdateFocusRing( self, self.callback.getBorderStyle );
|
||||
|
||||
sz := self.intercellSpacing;
|
||||
// Windows compatibility. on Windows there's no extra space between columns
|
||||
sz.width := 0;
|
||||
|
@ -245,7 +245,7 @@ begin
|
||||
scroll.setAutohidesScrollers(true);
|
||||
|
||||
ScrollViewSetBorderStyle(scroll, TCustomCheckListBox(AWinControl).BorderStyle);
|
||||
UpdateFocusRing(list, TCustomCheckListBox(AWinControl).BorderStyle);
|
||||
UpdateControlFocusRing(list, AWinControl);
|
||||
|
||||
Result := TLCLHandle(scroll);
|
||||
end;
|
||||
|
@ -478,6 +478,7 @@ begin
|
||||
TLCLCommonCallback(tv.callback.GetCallbackObject).BlockCocoaUpDown := true;
|
||||
lControl.callback := tv.callback;
|
||||
lControl.setView(tv);
|
||||
UpdateControlFocusRing( tabview, AWinControl );
|
||||
|
||||
Result := TLCLHandle(tv);
|
||||
end;
|
||||
|
@ -172,7 +172,7 @@ type
|
||||
const ABorderStyle: TBorderStyle); override;
|
||||
end;
|
||||
|
||||
procedure UpdateFocusRing(v: NSView; astyle: TBorderStyle);
|
||||
procedure UpdateControlFocusRing( cocoaControl: NSView; lclControl: TWinControl );
|
||||
procedure ScrollViewSetScrollStyles(AScroll: TCocoaScrollView; AStyles: TScrollStyle);
|
||||
procedure ScrollViewSetBorderStyle(sv: NSScrollView; astyle: TBorderStyle);
|
||||
|
||||
@ -221,6 +221,9 @@ implementation
|
||||
uses
|
||||
Math;
|
||||
|
||||
type
|
||||
TWinControlAccess = class(TWinControl);
|
||||
|
||||
var
|
||||
LastMouse: TLastMouseInfo;
|
||||
|
||||
@ -253,15 +256,28 @@ begin
|
||||
if AMouseButtons and (1 shl 4) <> 0 then Include(Result, ssExtra2);
|
||||
end;
|
||||
|
||||
procedure UpdateFocusRing(v: NSView; astyle: TBorderStyle);
|
||||
procedure UpdateControlFocusRing(cocoaControl: NSView; lclControl: TWinControl);
|
||||
const
|
||||
NSFocusRing : array [TBorderStyle] of NSBorderType = (
|
||||
NSFocusRingTypeNone, // bsNone
|
||||
NSFocusRingTypeDefault // bsSingle
|
||||
);
|
||||
var
|
||||
frs: CocoaConfig.TCocoaFocusRingStrategy;
|
||||
borderStyle: TBorderStyle;
|
||||
begin
|
||||
if Assigned(v) and CocoaHideFocusNoBorder then
|
||||
v.setFocusRingType( NSFocusRing[astyle] );
|
||||
frs:= CocoaConfig.getCocoaControlFocusRingStrategry( cocoaControl.className );
|
||||
case frs of
|
||||
TCocoaFocusRingStrategy.none:
|
||||
cocoaControl.setFocusRingType( NSFocusRingTypeNone );
|
||||
TCocoaFocusRingStrategy.required:
|
||||
cocoaControl.setFocusRingType( NSFocusRingTypeExterior );
|
||||
TCocoaFocusRingStrategy.border: begin
|
||||
borderStyle:= TWinControlAccess(lclControl).BorderStyle;
|
||||
cocoaControl.setFocusRingType( NSFocusRing[borderStyle] );
|
||||
end;
|
||||
// TCocoaFocusRingStrategy.default: no need to set FocusRing
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure ScrollViewSetScrollStyles(AScroll: TCocoaScrollView; AStyles: TScrollStyle);
|
||||
|
@ -162,7 +162,7 @@ begin
|
||||
if not Assigned(AWinControl) or not AWinControl.HandleAllocated then Exit;
|
||||
cocoaListView:= TCocoaListView(AWinControl.Handle);
|
||||
ScrollViewSetBorderStyle(cocoaListView.scrollView, ABorderStyle);
|
||||
UpdateFocusRing(cocoaListView.documentView, ABorderStyle);
|
||||
UpdateControlFocusRing(cocoaListView.documentView, AWinControl);
|
||||
end;
|
||||
|
||||
class procedure TCocoaWSCustomListView.ColumnDelete(const ALV: TCustomListView;
|
||||
|
@ -399,9 +399,17 @@ implementation
|
||||
|
||||
function AllocButton(const ATarget: TWinControl; const ACallBackClass: TLCLButtonCallBackClass; const AParams: TCreateParams; btnBezel: NSBezelStyle; btnType: NSButtonType): TCocoaButton;
|
||||
begin
|
||||
Result := TCocoaButton.alloc.lclInitWithCreateParams(AParams);
|
||||
if Assigned(Result) then
|
||||
begin
|
||||
case btnType of
|
||||
NSMomentaryLightButton,
|
||||
NSMomentaryChangeButton,
|
||||
NSMomentaryPushInButton:
|
||||
Result:= TCocoaButton.alloc;
|
||||
else
|
||||
Result:= TCocoaButtonNeedFocusRing.alloc;
|
||||
end;
|
||||
|
||||
if Assigned(Result) then begin
|
||||
Result:= Result.lclInitWithCreateParams(AParams);
|
||||
TCocoaButton(Result).callback := ACallBackClass.Create(Result, ATarget);
|
||||
|
||||
Result.setTitle(ControlTitleToNSStr(AParams.Caption));
|
||||
@ -848,7 +856,7 @@ class function TCocoaWSButton.CreateHandle(const AWinControl: TWinControl;
|
||||
var
|
||||
btn: TCocoaButton;
|
||||
begin
|
||||
btn := AllocButton(AWinControl, TLCLButtonCallback, AParams, NSRoundedBezelStyle, NSMomentaryPushInButton);
|
||||
btn := AllocButton(AWinControl, TLCLButtonCallback, AParams, NSRoundedBezelStyle, NSMomentaryLightButton);
|
||||
btn.smallHeight := PUSHBTN_SMALL_HEIGHT;
|
||||
btn.miniHeight := PUSHBTN_MINI_HEIGHT;
|
||||
btn.adjustFontToControlSize:=true;
|
||||
@ -1082,12 +1090,12 @@ end;
|
||||
|
||||
class function TCocoaWSCustomEdit.CreateHandle(const AWinControl: TWinControl; const AParams: TCreateParams): TLCLHandle;
|
||||
var
|
||||
field : NSTextField;
|
||||
field : TCocoaTextField;
|
||||
cell : NSTextFieldCell;
|
||||
begin
|
||||
if TCustomEdit(AWinControl).PasswordChar=#0
|
||||
then field:=NSTextField(AllocTextField(AWinControl, AParams))
|
||||
else field:=NSTextField(AllocSecureTextField(AWinControl, AParams));
|
||||
then field:=TCocoaTextField(AllocTextField(AWinControl, AParams))
|
||||
else field:=TCocoaTextField(AllocSecureTextField(AWinControl, AParams));
|
||||
if (field.respondsToSelector(ObjCSelector('cell'))) and Assigned(field.cell) then
|
||||
begin
|
||||
cell := NSTextFieldCell(field.cell);
|
||||
@ -1097,7 +1105,8 @@ begin
|
||||
if NOT TCocoaTextField(field).fixedBorderStyle then
|
||||
TextFieldSetBorderStyle(field, TCustomEdit(AWinControl).BorderStyle);
|
||||
TextFieldSetAllignment(field, TCustomEdit(AWinControl).Alignment);
|
||||
UpdateFocusRing(field, TCustomEdit(AWinControl).BorderStyle);
|
||||
if NOT field.fixedBorderStyle then
|
||||
UpdateControlFocusRing( field, AWinControl );
|
||||
|
||||
Result:=TLCLHandle(field);
|
||||
end;
|
||||
@ -1142,7 +1151,7 @@ begin
|
||||
field.setBordered( ABorderStyle <> bsNone );
|
||||
field.setBezeled( ABorderStyle <> bsNone );
|
||||
{$endif}
|
||||
UpdateFocusRing(field, ABorderStyle);
|
||||
UpdateControlFocusRing( field, AWinControl );
|
||||
end;
|
||||
|
||||
class function TCocoaWSCustomEdit.GetSelStart(const ACustomEdit: TCustomEdit): integer;
|
||||
@ -1637,7 +1646,7 @@ begin
|
||||
scr.setDrawsBackground(false);
|
||||
|
||||
ScrollViewSetBorderStyle(scr, TCustomMemo(AWinControl).BorderStyle);
|
||||
UpdateFocusRing(txt, TCustomMemo(AWinControl).BorderStyle);
|
||||
scr.setFocusRingType( NSFocusRingTypeExterior );
|
||||
|
||||
nr:=scr.documentVisibleRect;
|
||||
txt.setFrame(nr);
|
||||
@ -1660,7 +1669,7 @@ begin
|
||||
// This makes NSTextView to be responsive to theme color change (Mojave 10.14)
|
||||
txt.setTextColor(NSColor.textColor);
|
||||
txt.setBackgroundColor(NSColor.textBackgroundColor);
|
||||
scr.setFocusRingType(NSFocusRingTypeExterior);
|
||||
UpdateControlFocusRing(txt, AWinControl);
|
||||
|
||||
lcl := TLCLCommonCallback.Create(txt, AWinControl);
|
||||
lcl.ForceReturnKeyDown := true;
|
||||
@ -1730,7 +1739,7 @@ begin
|
||||
if not Assigned(sv) then Exit;
|
||||
|
||||
ScrollViewSetBorderStyle(sv, ABorderStyle);
|
||||
UpdateFocusRing(NSView(sv.documentView), ABorderStyle);
|
||||
UpdateControlFocusRing(sv.documentView, AWinControl);
|
||||
end;
|
||||
|
||||
class function TCocoaWSCustomMemo.GetCaretPos(const ACustomEdit: TCustomEdit): TPoint;
|
||||
@ -2512,7 +2521,7 @@ begin
|
||||
scroll.setHasHorizontalScroller(true);
|
||||
scroll.setAutohidesScrollers(true);
|
||||
ScrollViewSetBorderStyle(scroll, lclListBox.BorderStyle);
|
||||
UpdateFocusRing(list, lclListBox.BorderStyle);
|
||||
UpdateControlFocusRing(list, lclListBox);
|
||||
|
||||
Result := TLCLHandle(scroll);
|
||||
end;
|
||||
@ -2634,7 +2643,7 @@ begin
|
||||
if not Assigned(list) then Exit;
|
||||
|
||||
ScrollViewSetBorderStyle(list.enclosingScrollView, ABorderStyle);
|
||||
UpdateFocusRing(list, ABorderStyle);
|
||||
UpdateControlFocusRing(list, AWinControl);
|
||||
end;
|
||||
|
||||
class procedure TCocoaWSCustomListBox.SetItemIndex(const ACustomListBox: TCustomListBox; const AIndex: integer);
|
||||
|
Loading…
Reference in New Issue
Block a user