diff --git a/lcl/interfaces/cocoa/cocoacallback.pas b/lcl/interfaces/cocoa/cocoacallback.pas index 11f25acbc4..3fa12d8b88 100644 --- a/lcl/interfaces/cocoa/cocoacallback.pas +++ b/lcl/interfaces/cocoa/cocoacallback.pas @@ -88,20 +88,29 @@ type property CocoaOnlyState: Boolean read IsCocoaOnlyState write SetCocoaOnlyState; end; + { + currently the following callbacks implement IListViewCallBack, + need to be considered before modification: + 1. TLCLListViewCallback + 2. TLCLListBoxCallback + 3. TLCLCheckboxListCallback + } { IListViewCallBack } IListViewCallBack = interface(ICommonCallback) function ItemsCount: Integer; function GetImageListType( out lvil: TListViewImageList ): Boolean; function GetItemTextAt(ARow, ACol: Integer; var Text: String): Boolean; - function GetItemCheckedAt(ARow, ACol: Integer; var CheckState: Integer): Boolean; + function GetItemCheckedAt( row: Integer; var CheckState: Integer): Boolean; function GetItemImageAt(ARow, ACol: Integer; var imgIdx: Integer): Boolean; function GetImageFromIndex(imgIdx: Integer): NSImage; procedure SetItemTextAt(ARow, ACol: Integer; const Text: String); - procedure SetItemCheckedAt(ARow, ACol: Integer; CheckState: Integer); + procedure SetItemCheckedAt( row: Integer; CheckState: Integer); function shouldSelectionChange(NewSel: Integer): Boolean; procedure ColumnClicked(ACol: Integer); - function DrawRow(rowidx: Integer; ctx: TCocoaContext; const r: TRect; state: TOwnerDrawState): Boolean; + function drawItem( row: Integer; ctx: TCocoaContext; const r: TRect; state: TOwnerDrawState ): Boolean; + function customDraw( row: Integer; col: Integer; ctx: TCocoaContext; state: TCustomDrawState ): Boolean; + function isCustomDrawSupported: Boolean; procedure GetRowHeight(rowidx: Integer; var height: Integer); function GetBorderStyle: TBorderStyle; function onAddSubview( aView:NSView ): Boolean; diff --git a/lcl/interfaces/cocoa/cocoacollectionview.pas b/lcl/interfaces/cocoa/cocoacollectionview.pas index 1132e725d1..5fa2f84fef 100644 --- a/lcl/interfaces/cocoa/cocoacollectionview.pas +++ b/lcl/interfaces/cocoa/cocoacollectionview.pas @@ -605,7 +605,7 @@ begin cv:= TCocoaCollectionView( self.collectionView ); indexPath:= cv.indexPathForItem( self ); row:= indexPath.item; - cv.callback.SetItemCheckedAt( row, 0, sender.state ); + cv.callback.SetItemCheckedAt( row, sender.state ); if sender.state = NSOnState then begin cv.selectOneItemByIndex( row, True ); self.view.window.makeFirstResponder( self.collectionView ); @@ -811,7 +811,7 @@ begin checkBox:= cocoaItem.checkBox; if Assigned(checkBox) then begin - self.callback.GetItemCheckedAt( row, 0, checkedValue ); + self.callback.GetItemCheckedAt( row, checkedValue ); checkBox.setState( checkedValue ); checkBox.setHidden( NOT ((checkedValue=NSOnState) or isSelected) ); end; diff --git a/lcl/interfaces/cocoa/cocoalistview.pas b/lcl/interfaces/cocoa/cocoalistview.pas index ad007f8c59..5a8658821c 100644 --- a/lcl/interfaces/cocoa/cocoalistview.pas +++ b/lcl/interfaces/cocoa/cocoalistview.pas @@ -13,7 +13,7 @@ uses Controls, ComCtrls, Types, StdCtrls, LCLProc, Graphics, ImgList, Forms, // Cocoa WS CocoaPrivate, CocoaCallback, CocoaScrollers, CocoaWSScrollers, CocoaTextEdits, - CocoaWSCommon, cocoa_extra, CocoaGDIObjects; + CocoaWSCommon, cocoa_extra, CocoaGDIObjects, CocoaUtils; type { TLCLListViewCallback } @@ -33,17 +33,20 @@ type function ItemsCount: Integer; function GetImageListType( out lvil: TListViewImageList ): Boolean; function GetItemTextAt(ARow, ACol: Integer; var Text: String): Boolean; - function GetItemCheckedAt(ARow, ACol: Integer; var IsChecked: Integer): Boolean; + function GetItemCheckedAt( row: Integer; var IsChecked: Integer): Boolean; function GetItemImageAt(ARow, ACol: Integer; var imgIdx: Integer): Boolean; function GetImageFromIndex(imgIdx: Integer): NSImage; procedure SetItemTextAt(ARow, ACol: Integer; const Text: String); - procedure SetItemCheckedAt(ARow, ACol: Integer; IsChecked: Integer); + procedure SetItemCheckedAt( row: Integer; IsChecked: Integer); function getItemStableSelection(ARow: Integer): Boolean; procedure selectOne(ARow: Integer; isSelected:Boolean ); function shouldSelectionChange(NewSel: Integer): Boolean; procedure ColumnClicked(ACol: Integer); - function DrawRow(rowidx: Integer; ctx: TCocoaContext; const r: TRect; + function drawItem( row: Integer; ctx: TCocoaContext; const r: TRect; state: TOwnerDrawState): Boolean; + function customDraw( row: Integer; col: Integer; + ctx: TCocoaContext; state: TCustomDrawState ): Boolean; + function isCustomDrawSupported: Boolean; procedure GetRowHeight(rowidx: Integer; var h: Integer); function GetBorderStyle: TBorderStyle; function onAddSubview(aView: NSView): Boolean; @@ -301,15 +304,17 @@ begin end; end; -function TLCLListViewCallback.GetItemCheckedAt(ARow, ACol: Integer; +function TLCLListViewCallback.GetItemCheckedAt( row: Integer; var IsChecked: Integer): Boolean; var BoolState : array [Boolean] of Integer = (NSOffState, NSOnState); + indexSet: NSIndexSet; begin - if ownerData and Assigned(listView) and (ARow>=0) and (ARow < listView.Items.Count) then - IsChecked := BoolState[listView.Items[ARow].Checked] + indexSet:= self.checkedIndexSet; + if ownerData and Assigned(listView) and (row>=0) and (row < listView.Items.Count) then + IsChecked := BoolState[listView.Items[row].Checked] else - IsChecked := BoolState[checkedIndexSet.containsIndex(ARow)]; + IsChecked := BoolState[checkedIndexSet.containsIndex(row)]; Result := true; end; @@ -408,15 +413,15 @@ begin end; -procedure TLCLListViewCallback.SetItemCheckedAt(ARow, ACol: Integer; +procedure TLCLListViewCallback.SetItemCheckedAt( row: Integer; IsChecked: Integer); var Msg: TLMNotify; NMLV: TNMListView; begin if IsChecked = NSOnState - then checkedIndexSet.addIndex(ARow) - else checkedIndexSet.removeIndex(ARow); + then checkedIndexSet.addIndex(row) + else checkedIndexSet.removeIndex(row); FillChar(Msg{%H-}, SizeOf(Msg), #0); FillChar(NMLV{%H-}, SizeOf(NMLV), #0); @@ -425,7 +430,7 @@ begin NMLV.hdr.hwndfrom := ListView.Handle; NMLV.hdr.code := LVN_ITEMCHANGED; - NMLV.iItem := ARow; + NMLV.iItem := row; NMLV.iSubItem := 0; NMLV.uChanged := LVIF_STATE; Msg.NMHdr := @NMLV.hdr; @@ -504,19 +509,6 @@ begin LCLMessageGlue.DeliverMessage(ListView, Msg); end; -function TLCLListViewCallback.DrawRow(rowidx: Integer; ctx: TCocoaContext; - const r: TRect; state: TOwnerDrawState): Boolean; -var - ALV: TCustomListViewAccess; - drawResult: TCustomDrawResult; -begin - ALV:= TCustomListViewAccess(self.listView); - ALV.Canvas.Handle:= HDC(ctx); - drawResult:= ALV.IntfCustomDraw( dtItem, cdPrePaint, rowidx, 0, [], nil ); - Result:= cdrSkipDefault in drawResult; - ALV.Canvas.Handle:= 0; -end; - procedure TLCLListViewCallback.GetRowHeight(rowidx: Integer; var h: Integer); begin @@ -576,5 +568,50 @@ begin TCustomListViewAccess(Target).InitializeWnd; end; +function TLCLListViewCallback.drawItem( row: Integer; ctx: TCocoaContext; + const r: TRect; state: TOwnerDrawState ): Boolean; +var + Mess: TLMDrawListItem; + DrawStruct: TDrawListItemStruct; +begin + DrawStruct.ItemState := state; + DrawStruct.Area := r; + DrawStruct.DC := HDC(ctx); + DrawStruct.ItemID := row; + FillChar(Mess, SizeOf(Mess), 0); + Mess.Msg := CN_DRAWITEM; + Mess.DrawListItemStruct := @DrawStruct; + self.DeliverMessage( Mess ); + Result:= False; +end; + +function TLCLListViewCallback.customDraw(row: Integer; col: Integer; + ctx: TCocoaContext; state: TCustomDrawState ): Boolean; +var + ALV: TCustomListViewAccess; + drawTarget: TCustomDrawTarget; + drawResult: TCustomDrawResult; + rect: TRect; +begin + ALV:= TCustomListViewAccess(self.listView); + rect:= NSRectToRect( self.Owner.lclContentView.bounds ); + if col=0 then + drawTarget:= dtItem + else if col>0 then + drawTarget:= dtSubItem + else + drawTarget:= dtControl; + + ALV.Canvas.Handle:= HDC(ctx); + drawResult:= ALV.IntfCustomDraw( drawTarget, cdPrePaint, row, col, state, @rect ); + ALV.Canvas.Handle:= 0; + Result:= cdrSkipDefault in drawResult; +end; + +function TLCLListViewCallback.isCustomDrawSupported: Boolean; +begin + Result:= True; +end; + end. diff --git a/lcl/interfaces/cocoa/cocoatables.pas b/lcl/interfaces/cocoa/cocoatables.pas index 5516aee4a8..c5fbe6a33e 100644 --- a/lcl/interfaces/cocoa/cocoatables.pas +++ b/lcl/interfaces/cocoa/cocoatables.pas @@ -62,6 +62,8 @@ type procedure setColumn( column: NSTableColumn ); message 'setColumn:'; function checkBox: NSButton; message 'checkBox'; + procedure drawRect(dirtyRect: NSRect); override; + procedure loadView( row: Integer; col: Integer ); message 'loadView:col:'; procedure updateItemValue( row: NSInteger; col: NSInteger ); @@ -73,6 +75,17 @@ type procedure dealloc; override; end; + { + 1. TCocoaTableListView related need to support + TListView/TListBox/TCheckListBox, etc. + 2. the differences between these controls can be considered to be + implemented in the callback. + 3. however, after careful consideration, we tried to keep the original + intention of the callback, and added TCocoaTableViewProcessor to + isolate these differences. + } + { TCocoaTableViewProcessor } + TCocoaTableViewProcessor = class function isInitializing( tv: NSTableView ): Boolean; virtual; abstract; procedure onReloadData( tv: NSTableView ); virtual; abstract; @@ -135,8 +148,10 @@ type function fittingSize: NSSize; override; procedure drawRect(dirtyRect: NSRect); override; - function lclCustomDrawRow(row: NSInteger; clipRect: NSRect): Boolean; - message 'lclCustomDrawRow:clipRect:'; + function lclCallDrawItem( row: NSInteger; ctxSize: NSSize; clipRect: NSRect): Boolean; + message 'lclCallDrawItem:ctxSize:clipRect:'; + function lclCallCustomDraw( row: Integer; col: Integer; ctxSize: NSSize; clipRect: NSRect): Boolean; + message 'lclCallCustomDraw:col:ctxSize:clipRect:'; // mouse procedure mouseDown(event: NSEvent); override; @@ -336,21 +351,28 @@ begin Result := TCocoaTableListView.alloc; end; -procedure TCocoaTableRowView.drawRect(dirtyRect: NSRect); +procedure hideAllSubviews( parent: NSView ); var view: NSView; +begin + for view in parent.subviews do + view.setHidden( True ); +end; + +procedure TCocoaTableRowView.drawRect(dirtyRect: NSRect); +var done: Boolean; begin - inherited drawRect(dirtyRect); + done:= self.tableView.lclCallDrawItem( row , self.bounds.size, dirtyRect ); - done:= self.tableView.lclCustomDrawRow( row , dirtyRect ); if done then begin // the Cocoa default drawing cannot be skipped in NSTableView, // we can only hide the CellViews to get the same effect. // in the Lazarus IDE, there is a ListBox with OwnerDraw in Project-Forms, // it's a case where the default drawing must be skipped. - for view in self.subviews do - view.setHidden( True ); + hideAllSubviews( self ); + end else begin + inherited drawRect( dirtyRect ); end; end; @@ -452,34 +474,102 @@ begin Result:= NSZeroSize; end; -function TCocoaTableListView.lclCustomDrawRow(row: NSInteger; clipRect: NSRect - ): Boolean; +function isFocused( tv: TCocoaTableListView ; row: NSInteger ): Boolean; +begin + Result:= False; + if Assigned(tv.window) and (tv.window.firstResponder = tv) then begin + if row < 0 then + Result:= True + else if tv.isRowSelected(row) then + Result:= True; + end; +end; + +function isChecked( tv: TCocoaTableListView ; row: NSInteger ): Boolean; +var + checked: Integer; +begin + Result:= False; + if row < 0 then + Exit; + + tv.callback.GetItemCheckedAt( row, checked ); + Result:= checked=NSOnState; +end; + +function TCocoaTableListView.lclCallDrawItem(row: NSInteger; + ctxSize: NSSize; clipRect: NSRect ): Boolean; var ctx: TCocoaContext; ItemState: TOwnerDrawState; begin Result:= False; + if NOT self.isOwnerDraw then + Exit; + if not Assigned(callback) then Exit; ctx := TCocoaContext.Create(NSGraphicsContext.currentContext); - ctx.InitDraw(Round(bounds.size.width), Round(bounds.size.height)); + ctx.InitDraw(Round(ctxSize.width), Round(ctxSize.height)); try ItemState := []; if isRowSelected(row) then Include(ItemState, odSelected); - if lclIsEnabled then Include(ItemState, odDisabled); - if Assigned(window) and (window.firstResponder = self) and (odSelected in ItemState) then + if NOT lclIsEnabled then Include(ItemState, odDisabled); + if isFocused(self,row) then Include(ItemState, odFocused); + if isChecked(self,row) then + Include(ItemState, odChecked); - Result:= callback.DrawRow(row, ctx, NSRectToRect(clipRect), ItemState); + Result:= callback.drawItem(row, ctx, NSRectToRect(clipRect), ItemState); + finally + ctx.Free; + end; +end; + +function TCocoaTableListView.lclCallCustomDraw(row: Integer; col: Integer; + ctxSize: NSSize; clipRect: NSRect): Boolean; +var + ctx: TCocoaContext; + state: TCustomDrawState; +begin + Result:= False; + if NOT Assigned(callback) then + Exit; + if NOT callback.isCustomDrawSupported then + Exit; + + ctx := TCocoaContext.Create(NSGraphicsContext.currentContext); + ctx.InitDraw(Round(ctxSize.width), Round(ctxSize.height)); + try + state := []; + if isRowSelected(row) then Include(state, cdsSelected); + if NOT lclIsEnabled then Include(state, cdsDisabled); + if isFocused(self,row) then + Include(state, cdsFocused); + if isChecked(self,row) then + Include(state, cdsChecked); + + Result:= callback.customDraw(row, col, ctx, state); finally ctx.Free; end; end; procedure TCocoaTableListView.drawRect(dirtyRect: NSRect); +var + done: Boolean; begin - inherited drawRect(dirtyRect); if CheckMainThread and Assigned(callback) then callback.Draw(NSGraphicsContext.currentContext, bounds, dirtyRect); + + done:= self.lclCallCustomDraw( -1, -1, self.bounds.size, dirtyRect ); + + if done then begin + // the Cocoa default drawing cannot be skipped in NSTableView, + // we can only hide the SubviewViews to get the same effect. + hideAllSubviews( self ); + end else begin + inherited drawRect( dirtyRect ); + end; end; function TCocoaTableListView.getIndexOfColumn(ACol: NSTableColumn): Integer; @@ -951,6 +1041,28 @@ begin Result:= _checkBox; end; +procedure TCocoaTableListItem.drawRect(dirtyRect: NSRect); +var + row: Integer; + col: Integer; + done: Boolean; +begin + row:= _tableView.rowForView( self ); + col:= _tableView.columnForView( self ); + done:= TCocoaTableListView(_tableView).lclCallCustomDraw( + row, col, self.bounds.size, dirtyRect ); + + if done then begin + // the Cocoa default drawing cannot be skipped in NSTableView, + // we can only hide the CellViews to get the same effect. + // in the Lazarus IDE, there is a ListView with OnCustomDrawItem + // in Perferences-Component Palette. + hideAllSubviews( self ); + end else begin + inherited drawRect(dirtyRect); + end; +end; + procedure TCocoaTableListItem.createTextField; var fieldControl: NSTextField; @@ -1037,7 +1149,7 @@ begin lclcb:= tv.callback; if Assigned(_checkBox) then begin - lclcb.GetItemCheckedAt( row, 0, checkedValue ); + lclcb.GetItemCheckedAt( row, checkedValue ); _checkBox.setState( checkedValue ); end; @@ -1181,7 +1293,7 @@ begin if not Assigned(callback) then Exit; row := rowForView(sender.superview); - callback.SetItemCheckedAt(row, 0, sender.state); + callback.SetItemCheckedAt(row, sender.state); if sender.state = NSOnState then begin self.selectOneItemByIndex(row, True); self.window.makeFirstResponder( self ); @@ -1437,8 +1549,6 @@ begin begin if Assigned(item.imageView) then begin frame:= item.imageView.frame; - frame.origin.x:= frame.origin.x + item.frame.origin.x; - frame.origin.y:= frame.origin.y + item.frame.origin.y; end; end; end; @@ -1636,7 +1746,7 @@ begin {lvpHideSelection, lvpHotTrack,} lvpMultiSelect: _tableView.setAllowsMultipleSelection(AIsSet); - {lvpOwnerDraw,} + lvpOwnerDraw: _tableView.isOwnerDraw:= AIsSet; lvpReadOnly: _tableView.readOnly := AIsSet; { lvpRowSelect,} lvpShowColumnHeaders: diff --git a/lcl/interfaces/cocoa/cocoawschecklst.pas b/lcl/interfaces/cocoa/cocoawschecklst.pas index c0670fd702..a4be59438e 100644 --- a/lcl/interfaces/cocoa/cocoawschecklst.pas +++ b/lcl/interfaces/cocoa/cocoawschecklst.pas @@ -54,8 +54,8 @@ type public checklist: TCustomCheckListBox; constructor Create(AOwner: NSObject; ATarget: TWinControl; AHandleView: NSView); override; - function GetItemCheckedAt(ARow, ACol: Integer; var CheckState: Integer): Boolean; override; - procedure SetItemCheckedAt(ARow, ACol: Integer; CheckState: Integer); override; + function GetItemCheckedAt( row: Integer; var CheckState: Integer): Boolean; override; + procedure SetItemCheckedAt( row: Integer; CheckState: Integer); override; function GetCheckState(Index: Integer; var AState: Integer): Boolean; function SetCheckState(Index: Integer; AState: Integer; InvalidateCocoa: Boolean = true): Boolean; @@ -151,19 +151,19 @@ begin checklist := TCustomCheckListBox(ATarget); end; -function TLCLCheckboxListCallback.GetItemCheckedAt(ARow, ACol: Integer; +function TLCLCheckboxListCallback.GetItemCheckedAt( row: Integer; var CheckState: Integer): Boolean; begin - Result := GetCheckState(Arow, CheckState); + Result := GetCheckState(row, CheckState); end; -procedure TLCLCheckboxListCallback.SetItemCheckedAt(ARow, ACol: Integer; +procedure TLCLCheckboxListCallback.SetItemCheckedAt( row: Integer; CheckState: Integer); var changed : Boolean; begin - changed := SetCheckState(ARow, CheckState, false); // returns true, if changed!s - if changed then LCLSendChangedMsg(Target, ARow); + changed := SetCheckState(row, CheckState, false); // returns true, if changed!s + if changed then LCLSendChangedMsg(Target, row); end; function TLCLCheckboxListCallback.GetCheckState(Index: Integer; var AState: Integer): Boolean; diff --git a/lcl/interfaces/cocoa/cocoawsstdctrls.pas b/lcl/interfaces/cocoa/cocoawsstdctrls.pas index 8f25e1e2ba..88fd09b45b 100644 --- a/lcl/interfaces/cocoa/cocoawsstdctrls.pas +++ b/lcl/interfaces/cocoa/cocoawsstdctrls.pas @@ -280,14 +280,18 @@ type function ItemsCount: Integer; virtual; function GetImageListType(out lvil: TListViewImageList): Boolean; virtual; function GetItemTextAt(ARow, ACol: Integer; var Text: String): Boolean; virtual; - function GetItemCheckedAt(ARow, ACol: Integer; var isChecked: Integer): Boolean; virtual; + function GetItemCheckedAt( row: Integer; var isChecked: Integer): Boolean; virtual; function GetItemImageAt(ARow, ACol: Integer; var imgIdx: Integer): Boolean; virtual; function GetImageFromIndex(imgIdx: Integer): NSImage; virtual; procedure SetItemTextAt(ARow, ACol: Integer; const Text: String); virtual; - procedure SetItemCheckedAt(ARow, ACol: Integer; isChecked: Integer); virtual; + procedure SetItemCheckedAt( row: Integer; isChecked: Integer); virtual; function shouldSelectionChange(NewSel: Integer): Boolean; virtual; procedure ColumnClicked(ACol: Integer); virtual; - function DrawRow(rowidx: Integer; ctx: TCocoaContext; const r: TRect; state: TOwnerDrawState): Boolean; virtual; + function drawItem( row: Integer; ctx: TCocoaContext; const r: TRect; + state: TOwnerDrawState ): Boolean; virtual; + function customDraw( row: Integer; col: Integer; + ctx: TCocoaContext; state: TCustomDrawState ): Boolean; virtual; + function isCustomDrawSupported: Boolean; virtual; procedure GetRowHeight(rowidx: integer; var h: Integer); virtual; function GetBorderStyle: TBorderStyle; function onAddSubview(aView: NSView): Boolean; @@ -625,7 +629,7 @@ begin if Result then Text := strings[ARow]; end; -function TLCLListBoxCallback.GetItemCheckedAt(ARow, ACol: Integer; +function TLCLListBoxCallback.GetItemCheckedAt( row: Integer; var isChecked: Integer): Boolean; begin Result := false; @@ -648,7 +652,7 @@ begin // todo: end; -procedure TLCLListBoxCallback.SetItemCheckedAt(ARow, ACol: Integer; +procedure TLCLListBoxCallback.SetItemCheckedAt( row: Integer; isChecked: Integer); begin // do nothing @@ -665,8 +669,8 @@ begin // not needed end; -function TLCLListBoxCallback.DrawRow(rowidx: Integer; ctx: TCocoaContext; - const r: TRect; state: TOwnerDrawState): Boolean; +function TLCLListBoxCallback.drawItem( row: Integer; ctx: TCocoaContext; + const r: TRect; state: TOwnerDrawState ): Boolean; var DrawStruct: TDrawListItemStruct; begin @@ -677,11 +681,22 @@ begin DrawStruct.ItemState := state; DrawStruct.Area := r; DrawStruct.DC := HDC(ctx); - DrawStruct.ItemID := rowIdx; + DrawStruct.ItemID := row; LCLSendDrawListItemMsg(Target, @DrawStruct); Result:= True; end; +function TLCLListBoxCallback.customDraw(row: Integer; col: Integer; + ctx: TCocoaContext; state: TCustomDrawState ): Boolean; +begin + Result:= False; +end; + +function TLCLListBoxCallback.isCustomDrawSupported: Boolean; +begin + Result:= False; +end; + procedure TLCLListBoxCallback.GetRowHeight(rowidx: integer; var h: Integer); begin if TCustomListBox(Target).Style = lbOwnerDrawVariable then