1 { $Id: $}
2 {                  --------------------------------------------
3                   cocoatabcontrols.pas  -  Cocoa internal classes
4                   --------------------------------------------
5 
6  This unit contains the private classhierarchy for the Cocoa implemetations
7 
8  *****************************************************************************
9   This file is part of the Lazarus Component Library (LCL)
10 
11   See the file COPYING.modifiedLGPL.txt, included in this distribution,
12   for details about the license.
13  *****************************************************************************
14 }
15 unit CocoaTabControls;
16 
17 {$mode objfpc}{$H+}
18 {$modeswitch objectivec1}
19 {$modeswitch objectivec2}
20 {$interfaces corba}
21 {$include cocoadefines.inc}
22 
23 interface
24 
25 uses
26   // rtl+ftl
27   Types, Classes, SysUtils,
28   CGGeometry,
29   // Libs
30   MacOSAll, CocoaAll, CocoaUtils, //CocoaGDIObjects,
31   cocoa_extra, CocoaPrivate;
32 
33 type
34 
35   ITabControlCallback = interface(ICommonCallback)
36     procedure willSelectTabViewItem(aTabIndex: Integer);
37     procedure didSelectTabViewItem(aTabIndex: Integer);
38   end;
39 
40   { TCocoaTabPage }
41 
42   TCocoaTabPage = objcclass(NSTabViewItem)
43   public
44     callback: ICommonCallback;
lclGetCallbacknull45     function lclGetCallback: ICommonCallback; override;
46     procedure lclClearCallback; override;
lclFramenull47     function lclFrame: TRect; override;
lclClientFramenull48     function lclClientFrame: TRect; override;
49     procedure setLabel(label__: NSString); override;
50   end;
51 
52   { TCocoaTabControl }
53 
54   TCocoaTabControl = objcclass(NSTabView, NSTabViewDelegateProtocol)
55   private
56     prevarr  : NSButton;
57     nextarr  : NSButton;
58 
59   public
60     leftmost : Integer;         // index of the left-most tab shown
61     ignoreChange: Boolean;
62     callback: ITabControlCallback;
63 
64     fulltabs : NSMutableArray;  // the full list of NSTabViewItems
65     lclEnabled: Boolean;
66     // cocoa
allocnull67     class function alloc: id; override;
68     procedure setFrame(aframe: NSRect); override;
69     // lcl
lclIsEnablednull70     function lclIsEnabled: Boolean; override;
71     procedure lclSetEnabled(AEnabled: Boolean); override;
lclGetCallbacknull72     function lclGetCallback: ICommonCallback; override;
73     procedure lclClearCallback; override;
lclClientFramenull74     function lclClientFrame: TRect; override;
lclGetFrameToLayoutDeltanull75     function lclGetFrameToLayoutDelta: TRect; override;
76     // NSTabViewDelegateProtocol
tabView_shouldSelectTabViewItemnull77     function tabView_shouldSelectTabViewItem(tabView: NSTabView; tabViewItem: NSTabViewItem): Boolean; message 'tabView:shouldSelectTabViewItem:';
78     procedure tabView_willSelectTabViewItem(tabView: NSTabView; tabViewItem: NSTabViewItem); message 'tabView:willSelectTabViewItem:';
79     procedure tabView_didSelectTabViewItem(tabView: NSTabView; tabViewItem: NSTabViewItem); message 'tabView:didSelectTabViewItem:';
80     procedure tabViewDidChangeNumberOfTabViewItems(TabView: NSTabView); message 'tabViewDidChangeNumberOfTabViewItems:';
81     // mouse events
82     procedure mouseDown(event: NSEvent); override;
83     procedure mouseUp(event: NSEvent); override;
84     procedure rightMouseDown(event: NSEvent); override;
85     procedure rightMouseUp(event: NSEvent); override;
86     procedure rightMouseDragged(event: NSEvent); override;
87     procedure otherMouseDown(event: NSEvent); override;
88     procedure otherMouseUp(event: NSEvent); override;
89     procedure otherMouseDragged(event: NSEvent); override;
90     procedure mouseDragged(event: NSEvent); override;
91     procedure mouseMoved(event: NSEvent); override;
92     // lcl
93     procedure exttabInsertTabViewItem_atIndex(lTabPage: NSTabViewItem; AIndex: integer);
94       message 'exttabInsertTabViewItem:atIndex:';
95     procedure exttabRemoveTabViewItem(lTabPage: NSTabViewItem);
96       message 'exttabRemoveTabViewItem:';
exttabIndexOfTabViewItemnull97     function exttabIndexOfTabViewItem(lTabPage: NSTabViewItem): NSInteger;
98       message 'exttabIndexOfTabViewItem:';
99     procedure extTabPrevButtonClick(sender: id);
100       message 'extTabPrevButtonClick:';
101     procedure extTabNextButtonClick(sender: id);
102       message 'extTabNextButtonClick:';
103     procedure extselectTabViewItemAtIndex(index: NSInteger);
104       message 'extselectTabViewItemAtIndex:';
105   end;
106 
107   { TCocoaTabPageView }
108 
109   TCocoaTabPageView = objcclass(TCocoaCustomControl)
110   public
111     tabView: TCocoaTabControl;
112     tabPage: TCocoaTabPage;
113     procedure setFrame(arect: NSRect); override;
114   end;
115 
IndexOfTabnull116 function IndexOfTab(ahost: TCocoaTabControl; atab: NSTabViewItem): Integer;
117 
attemptsnull118 // Hack: The function attempts to determine the tabs view
119 // if the view is found it would return its frame rect in LCL coordinates
120 // if the view cannot be determinted, the function returns false
121 // This is implemented as ObjC method, because "prevarr" and "nextarr"
122 // are private methods.
123 // It's unknown, if it's safe to cache the result, so the search is performed
124 // everytime
125 function GetTabsRect(tabs: TCocoaTabControl; var r: TRect): Boolean;
126 
127 implementation
128 
GetTabsRectnull129 function GetTabsRect(tabs: TCocoaTabControl; var r: TRect): Boolean;
130 var
131   i  : integer;
132   sv : NSView;
133   f  : NSRect;
134 begin
135   Result:=Assigned(tabs);
136   if not Result then Exit;
137 
138   for i:=0 to Integer(tabs.subviews.count)-1 do
139   begin
140     sv:=NSView(tabs.subviews.objectAtIndex(i));
141     if not Assigned(sv)
142        or (sv = tabs.nextarr)
143        or (sv = tabs.prevarr)
144        or (sv.isKindOfClass(TCocoaTabPageView))
145     then Continue;
146 
147     f := sv.frame;
148     if tabs.isFlipped then
149       r := NSRectToRect( f )
150     else
151       NSToLCLRect( f, tabs.frame.size.height, r );
152     Result := true;
153     Exit;
154   end;
155   Result:=false;
156 end;
157 
AllocArrowButtonnull158 function AllocArrowButton(isPrev: Boolean): NSButton;
159 var
160   btn : NSButton;
161   r   : NSRect;
162 begin
163   btn:=NSButton(NSButton.alloc).initWithFrame(NSZeroRect);
164   btn.setBezelStyle(NSRegularSquareBezelStyle);
165   btn.setButtonType(NSMomentaryLightButton);
166 
167   if isPrev then
168     btn.setImage( NSImage.imageNamed( NSImageNameLeftFacingTriangleTemplate  ))
169   else
170     btn.setImage( NSImage.imageNamed( NSImageNameRightFacingTriangleTemplate ));
171 
172   {$ifdef BOOLFIX}
173   btn.setBordered_(Ord(false));
174   {$else}
175   btn.setBordered(false);
176   {$endif}
177   btn.setTitle(NSString.string_);
178   btn.sizeToFit();
179   if not isPrev then btn.setAutoresizingMask(NSViewMinXMargin);
180   Result:=btn;
181 end;
182 
183 const
184   arrow_hofs = 12;
185   arrow_vofs = 20;
186 
187 procedure PlaceButton(isPrev: Boolean; abtn: NSButton; dst: NSTabView);
188 var
189   org: NSPoint;
190 begin
191   if not assigned(abtn) then Exit;
192 
193   if isPrev then org:=NSMakePoint(arrow_hofs, arrow_vofs)
194   else org:=NSMakePoint(dst.frame.size.width - abtn.frame.size.width - arrow_hofs , arrow_vofs);
195 
196   abtn.setFrameOrigin(org);
197 end;
198 
199 
200 procedure AllocPrevNext(aview: TCocoaTabControl);
201 begin
202   aview.prevarr := AllocArrowButton(true);
203   aview.addSubview(aview.prevarr);
204   aview.nextarr := AllocArrowButton(false);
205   aview.addSubview(aview.nextarr);
206   aview.prevarr.setTarget(aview);
207   aview.prevarr.setAction( ObjCSelector('extTabPrevButtonClick:'));
208   aview.nextarr.setTarget(aview);
209   aview.nextarr.setAction( ObjCSelector('extTabNextButtonClick:'));
210 
211 
212   PlaceButton(true, aview.prevarr, aview);
213   PlaceButton(false, aview.nextarr, aview);
214 end;
215 
216 procedure RemoveAllTabs(aview: TCocoaTabControl);
217 var
218   arr: NSArray;
219   i : integer;
220 begin
221   arr := aview.tabViewItems;
222   for i := Integer(arr.count) - 1  downto 0 do
223     aview.removeTabViewItem( arr.objectAtIndex(i) );
224 end;
225 
226 procedure AttachAllTabs(aview: TCocoaTabControl);
227 var
228   i : integer;
229 begin
230   RemoveAllTabs(aview);
231   for i := 0 to aview.fulltabs.count - 1 do
232     aview.insertTabViewItem_atIndex( aview.fulltabs.objectAtIndex(i), i);
233 end;
234 
235 procedure ReviseTabs(aview: TCocoaTabControl; out ShowPrev,ShowNext: Boolean);
236 var
237   minw: double;
238   i: integer;
239   arr: NSArray;
240   vi : NSTabViewItem;
241   sz : NSSize;
242   x,y: integer;
243   lw  : double;
244   tbext : double;
245   lwid : array of double;
246   xd  : double;
247   j : integer;
248   ofs : integer;
249   frw: double;
250   sel: NSTabViewItem;
251   v : NSView;
252 begin
253   ShowPrev := false;
254   ShowNext := false;
255 
256   sel := aview.selectedTabViewItem;
257   try
258     if (aview.fulltabs.count>aview.tabViewItems.count) then
259       AttachAllTabs(aview);
260 
261     minw := aview.minimumSize.width;
262     if (minw<aview.frame.size.width) then Exit;
263 
264     arr := aview.tabViewItems;
265 
266     lw := 0;
267     SetLength(lwid, arr.count);
268     for i := 0 to arr.count - 1 do
269     begin
270       vi := NSTabViewItem( arr.objectAtIndex(i) );
271       sz := vi.sizeOfLabel(false);
272       lw := lw + sz.width;
273       lwid[i] := sz.width;
274     end;
275 
276     tbext := (minw - lw) / arr.count;
277     for i:=0 to length(lwid)-1 do
278       lwid[i] := lwid[i] + tbext;
279 
280     ofs := aview.leftmost;
281     if ofs>=length(lwid) then ofs:=length(lwid)-1;
282     if (ofs < 0) then Exit;
283     ShowPrev := ofs > 0;
284 
285 
286     xd := lwid[ofs];
287     frw := aview.frame.size.width;
288     frw := frw - ((arrow_hofs + aview.nextarr.frame.size.width) * 2);
289     if frw<0 then frw := 0;
290 
291     //aview.prevarr.isHidden((aview.leftmost=0));
292 
293     for i := ofs+1 to length(lwid)-1 do begin
294       if xd + lwid[i] > frw then begin
295         //aview.nextarr.isHidden((aview.leftmost=0));
296         for j:=length(lwid)-1 downto i do
297           aview.removeTabViewItem( arr.objectAtIndex(j));
298         ShowNext := true;
299         Break;
300       end;
301       xd := xd + lwid[i];
302     end;
303 
304     if not ShowNext then begin
305       // shown all right-side tabs, there might be a tab, that can be shown on the left
306       while (ofs>0) and (xd+lwid[ofs]<frw) do begin
307         xd := xd + lwid[ofs];
308         dec(ofs);
309       end;
310       aview.leftmost:=ofs;
311     end;
312 
313     for i := ofs - 1 downto 0 do
314       aview.removeTabViewItem( arr.objectAtIndex(i));
315 
316     // todo: automatic resizing still has its effect on the tabs.
317     //       the content page fails to pick up the proper size and place
318     //       and it seems that the content of the tab is shifted.
319     //       Needs investiage and a fix.
320 
321   finally
322     if (aview.indexOfTabViewItem(sel)<>NSNotFound) then
323       aview.selectTabViewItem(sel);
324   end;
325 end;
326 
327 procedure UpdateTabAndArrowVisibility(aview: TCocoaTabControl);
328 var
329   showNext : Boolean;
330   showPrev : Boolean;
331 begin
332   ReviseTabs(aview, showPrev, showNExt);
333   if Assigned(aview.prevarr) then
334     {$ifdef BOOLFIX}
335     aview.prevarr.setHidden_(Ord(not showPrev));
336     {$else}
337     aview.prevarr.setHidden(not showPrev);
338     {$endif}
339   if Assigned(aview.nextarr) then
340     {$ifdef BOOLFIX}
341     aview.nextarr.setHidden_(Ord(not showNext));
342     {$else}
343     aview.nextarr.setHidden(not showNext);
344     {$endif}
345 end;
346 
IndexOfTabnull347 function IndexOfTab(ahost: TCocoaTabControl; atab: NSTabViewItem): Integer;
348 var
349   idx : NSUInteger;
350 begin
351   idx := ahost.fulltabs.indexOfObject(atab);
352   if idx=NSUIntegerMax then Result:=-1
353   else begin
354     if idx>MaxInt then Result:=-1
355     else Result:=Integer(idx);
356   end;
357 end;
358 
359 { TCocoaTabPageView }
360 
361 procedure TCocoaTabPageView.setFrame(arect: NSRect);
362 begin
363   // It's possible for a tab page view to go in negative height.
364   // However, automatic resizing flags (for whatever reason) prevent
365   // TCocoaTabPageView to go into negative height (remaining at 0 pixels)
366   // The code below makes sure that resizing is actually happening
367   if Assigned(superView) and (superView.frame.size.height < arect.size.height) then
368     arect.size.height := superView.frame.size.height;
369 
370   inherited setFrame(arect);
371 end;
372 
373 { TCocoaTabPage }
374 
lclGetCallbacknull375 function TCocoaTabPage.lclGetCallback: ICommonCallback;
376 begin
377   Result := callback;
378 end;
379 
380 procedure TCocoaTabPage.lclClearCallback;
381 begin
382   callback := nil;
383 end;
384 
lclFramenull385 function TCocoaTabPage.lclFrame: TRect;
386 var
387   svh: CGFloat;
388   lParent: NSTabView;
389 begin
390   lParent := tabView;
391   if lParent <> nil then
392   begin
393     svh := lParent.contentRect.size.height;
394     NSToLCLRect(lParent.contentRect, svh, Result);
395   end
396   else
397   begin
398     svh := tabView.frame.size.height;
399     NSToLCLRect(tabView.contentRect, svh, Result);
400   end;
401   {$IFDEF COCOA_DEBUG_TABCONTROL}
402   WriteLn('[TCocoaTabPage.lclFrame] '+dbgs(Result)+' '+NSStringToString(Self.label_));
403   {$ENDIF}
404 end;
405 
lclClientFramenull406 function TCocoaTabPage.lclClientFrame: TRect;
407 begin
408   Result := lclFrame();
409 end;
410 
411 procedure TCocoaTabPage.setLabel(label__: NSString);
412 begin
413   inherited setLabel(label__);
414   //todo: revise the parent labels
415 end;
416 
417 { TCocoaTabControl }
418 
419 class function TCocoaTabControl.alloc: id;
420 begin
421   Result := inherited alloc;
422   TCocoaTabControl(Result).fulltabs := NSMutableArray(NSMutableArray.alloc).init;
423 end;
424 
425 procedure TCocoaTabControl.setFrame(aframe: NSRect);
426 begin
427   inherited setFrame(aframe);
428 
429   if not Assigned(nextarr) then
430     AllocPrevNext( self );
431 
432   UpdateTabAndArrowVisibility(self);
433 end;
434 
435 procedure TCocoaTabControl.extselectTabViewItemAtIndex(index: NSInteger);
436 var
437   idx : integer;
438   itm : NSTabViewItem;
439   i   : NSUInteger;
440 begin
441   if (index<0) or (index>=fulltabs.count) then Exit;
442   itm := NSTabViewItem(fulltabs.objectAtIndex(index));
443 
444   i := tabViewItems.indexOfObject(itm);
445   if i <> NSNotFound then
446   begin
447     inherited selectTabViewItemAtIndex(NSInteger(i));
448   end
449   else begin
450     leftmost := index;
451     UpdateTabAndArrowVisibility(self);
452     i := tabViewItems.indexOfObject(itm);
453     inherited selectTabViewItemAtIndex(NSInteger(i));
454   end;
455 end;
456 
lclIsEnablednull457 function TCocoaTabControl.lclIsEnabled: Boolean;
458 begin
459   Result:=lclEnabled and ((Assigned(superview) and superview.lclIsEnabled) or not Assigned(superview));
460 end;
461 
462 procedure TCocoaTabControl.lclSetEnabled(AEnabled: Boolean);
463 begin
464   lclEnabled := AEnabled;
465   inherited lclSetEnabled(AEnabled);
466 end;
467 
lclGetCallbacknull468 function TCocoaTabControl.lclGetCallback: ICommonCallback;
469 begin
470   Result := callback;
471 end;
472 
473 procedure TCocoaTabControl.lclClearCallback;
474 begin
475   callback := nil;
476 end;
477 
lclClientFramenull478 function TCocoaTabControl.lclClientFrame: TRect;
479 var
480   r : TRect;
481   f : NSRect;
482 begin
483   case tabViewType of
484     NSNoTabsNoBorder:
485     begin
486       f := frame;
487       f.origin.x := 0;
488       f.origin.y := 0;
489       Result := NSRectToRect( f );
490     end;
491   else
492     if isFlipped then
493       Result:=NSRectToRect( contentRect )
494     else
495       NSToLCLRect( contentRect, frame.size.height, Result );
496   end;
497 
498   //if tabs are hidden, frame layout should not be taken into account
499   //r:=lclGetFrameToLayoutDelta;
500   //Types.OffsetRect(Result, -r.Left, -r.Top);
501 end;
502 
lclGetFrameToLayoutDeltanull503 function TCocoaTabControl.lclGetFrameToLayoutDelta: TRect;
504 begin
505   case tabViewType of
506     NSNoTabsNoBorder: begin
507       Result.Left := 0;
508       Result.Top := 0;
509       Result.Bottom := 0;
510       Result.Right := 0;
511     end;
512   else
513     Result.Bottom := -10;
514     Result.Top := 6;
515     Result.Left := 7;
516     Result.Right := -7;
517   end;
518 end;
519 
tabView_shouldSelectTabViewItemnull520 function TCocoaTabControl.tabView_shouldSelectTabViewItem(tabView: NSTabView;
521   tabViewItem: NSTabViewItem): Boolean;
522 begin
523   Result := True;
524 end;
525 
526 procedure TCocoaTabControl.tabView_willSelectTabViewItem(tabView: NSTabView;
527   tabViewItem: NSTabViewItem);
528 begin
529   if ignoreChange then Exit;
530   if Assigned(callback) then
531   begin
532     callback.willSelectTabViewItem( IndexOfTab( self, tabViewItem) );
533   end;
534 end;
535 
536 procedure TCocoaTabControl.tabView_didSelectTabViewItem(tabView: NSTabView;
537   tabViewItem: NSTabViewItem);
538 begin
539   //it's called together with "willSelect"
540 
541   if Assigned(callback) then
542   begin
543     // Expected LCL Focus changing goes as following:
544     //   First page becomes visible
545     //   Then focus is switching to the control of the page
546     // In Cocoa world, first "willSelect" runs,
547     //  then "firstResponder" changes to Window
548     //  then the views are reorded and the new View becomes a part
549     //  of views chain (and attaches to the window
550     //  the view is made "firstResponder"
551     //  and finally "didSelect" is fired
552     callback.didSelectTabViewItem( IndexOfTab( self, tabViewItem) );
553   end;
554 
555   // The recent clean up, drove the workaround below unnecessary
556   // (at least the problem is not observed)
557   // The issue, is that the controls are being placed incorrectly, below
558   // the actual height of the control. Refactoring, removed direct LCL bindings.
559   // And it seemed to helped with returning invalid control bounds?!
560 
561   // Update the coordinates of all children of this tab
562   // Fixes bug 31914: TPageControl problems with Cocoa
563   {lTabView := tabViewItem.view.subViews.objectAtIndex(0);
564   for i := 0 to lTabView.subViews.count-1 do
565   begin
566     lCurSubview := lTabView.subViews.objectAtIndex(i);
567     lCurCallback := lCurSubview.lclGetCallback();
568     if Assigned(lCurCallback) then
569     begin
570       lLCLControl := TWinControl(lCurCallback.GetTarget());
571       lBounds := Classes.Bounds(lLCLControl.Left, lLCLControl.Top, lLCLControl.Width, lLCLControl.Height);
572       lCurSubview.lclSetFrame(lBounds);
573     end;
574   end;}
575 end;
576 
577 procedure TCocoaTabControl.tabViewDidChangeNumberOfTabViewItems(
578   TabView: NSTabView);
579 begin
580 
581 end;
582 
583 procedure TCocoaTabControl.mouseDown(event: NSEvent);
584 var
585   itm : NSTabViewItem;
586 begin
587   itm := self.tabViewItemAtPoint( self.convertPoint_fromView(event.locationInWindow, nil ));
588   if Assigned(itm) then
589   begin
590     inherited mouseDown(event);
591     Exit;
592   end;
593 
594   if not (Assigned(callback) and callback.MouseUpDownEvent(event, false, true)) then
595   begin
596     inherited mouseDown(event);
597 
598     if Assigned(callback) then
599     begin
600       callback.MouseUpDownEvent(event, True);
601     end;
602   end
603 end;
604 
605 procedure TCocoaTabControl.mouseUp(event: NSEvent);
606 begin
607   if not Assigned(callback) then callback.MouseUpDownEvent(event);
608   inherited mouseUp(event);
609 end;
610 
611 procedure TCocoaTabControl.rightMouseDown(event: NSEvent);
612 begin
613   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
614     inherited rightMouseDown(event);
615 end;
616 
617 procedure TCocoaTabControl.rightMouseUp(event: NSEvent);
618 begin
619   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
620     inherited rightMouseUp(event);
621 end;
622 
623 procedure TCocoaTabControl.rightMouseDragged(event: NSEvent);
624 begin
625   if not Assigned(callback) or not callback.MouseMove(event) then
626     inherited rightMouseDragged(event);
627 end;
628 
629 procedure TCocoaTabControl.otherMouseDown(event: NSEvent);
630 begin
631   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
632     inherited otherMouseDown(event);
633 end;
634 
635 procedure TCocoaTabControl.otherMouseUp(event: NSEvent);
636 begin
637   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
638     inherited otherMouseUp(event);
639 end;
640 
641 procedure TCocoaTabControl.otherMouseDragged(event: NSEvent);
642 begin
643   if not Assigned(callback) or not callback.MouseMove(event) then
644     inherited otherMouseDragged(event);
645 end;
646 
647 procedure TCocoaTabControl.mouseDragged(event: NSEvent);
648 begin
649   if not Assigned(callback) or not callback.MouseMove(event) then
650     inherited mouseDragged(event);
651 end;
652 
653 procedure TCocoaTabControl.mouseMoved(event: NSEvent);
654 begin
655   if Assigned(callback) then callback.MouseMove(event);
656   inherited mouseMoved(event);
657 end;
658 
659 procedure TCocoaTabControl.exttabInsertTabViewItem_atIndex(
660   lTabPage: NSTabViewItem; AIndex: integer);
661 begin
662   if AIndex>fulltabs.count then AIndex:=fulltabs.count;
663   fulltabs.insertObject_atIndex(lTabPage, AIndex);
664 
665   UpdateTabAndArrowVisibility(self);
666 end;
667 
668 procedure TCocoaTabControl.exttabRemoveTabViewItem(lTabPage: NSTabViewItem);
669 var
670   idx : NSInteger;
671 begin
672   idx := indexOfTabViewItem(lTabPage);
673   if (idx>=0) and (idx<>NSNotFound) then
674     removeTabViewItem(lTabPage);
675 
676   fulltabs.removeObject(lTabPage);
677 
678   UpdateTabAndArrowVisibility(self);
679 end;
680 
exttabIndexOfTabViewItemnull681 function TCocoaTabControl.exttabIndexOfTabViewItem(lTabPage: NSTabViewItem
682   ): NSInteger;
683 begin
684   Result := fulltabs.indexOfObject(lTabPage);
685 end;
686 
687 procedure TCocoaTabControl.extTabPrevButtonClick(sender: id);
688 begin
689   if leftmost = 0 then Exit;
690 
691   leftmost := leftmost - 1;
692   UpdateTabAndArrowVisibility(self);
693 end;
694 
695 procedure TCocoaTabControl.extTabNextButtonClick(sender: id);
696 begin
697   if leftmost = fulltabs.count - 1 then Exit;
698 
699   leftmost := leftmost + 1;
700   UpdateTabAndArrowVisibility(self);
701 end;
702 
703 
704 end.
705 
706