1{%MainUnit win32wscomctrls.pp} 2{ 3 ***************************************************************************** 4 This file is part of the Lazarus Component Library (LCL) 5 6 See the file COPYING.modifiedLGPL.txt, included in this distribution, 7 for details about the license. 8 ***************************************************************************** 9} 10 11type 12 TCustomPageAccess = class(TCustomPage) 13 end; 14 15function IsTabControlGroupFocused(const ATabControl: TCustomTabControl): boolean; 16var 17 Handle, FocusHandle: HWND; 18begin 19 Result := False; 20 if not ATabControl.HandleAllocated then exit; 21 Handle := ATabControl.Handle; 22 FocusHandle := Windows.GetFocus; 23 while (FocusHandle <> 0) and (FocusHandle <> Handle) do 24 FocusHandle := Windows.GetParent(FocusHandle); 25 if FocusHandle = 0 then exit; 26 Result := True; 27end; 28 29{ sets focus to a control on the newly focused tab page } 30procedure TabControlFocusNewControl(const ATabControl: TCustomTabControl; NewIndex: integer); 31var 32 Page: TCustomPage; 33 AWinControl: TWinControl; 34 ParentForm: TCustomForm; 35begin 36 { see if currently focused control is within tab control } 37 if not IsTabControlGroupFocused(ATabControl) then exit; 38 39 { focus was/is within tab control, pick a new control to focus } 40 Page := ATabControl.CustomPage(NewIndex); 41 ParentForm := GetParentForm(ATabControl); 42 if ParentForm <> nil then 43 begin 44 if ATabControl.ContainsControl(ParentForm.ActiveControl) and (ParentForm.ActiveControl <> ATabControl) then 45 begin 46 // if ActiveControl is already on active page 47 if Page.IsParentOf(ParentForm.ActiveControl) then Exit; 48 AWinControl := nil; 49 if Page.CanFocus then 50 AWinControl := TCustomPageAccess(Page).FindNextControl(nil, True, True, False); 51 // if nothing to focus then focus tab control then we can traverse pages by keys 52 if AWinControl = nil then 53 AWinControl := ATabControl; 54 AWinControl.SetFocus; 55 end; 56 end; 57end; 58 59function ShowHideTabPage(TabControlHandle: HWnd; Showing: boolean): integer; 60const 61 ShowFlags: array[Boolean] of DWord = (SWP_HIDEWINDOW or SWP_NOZORDER or SWP_NOREDRAW, SWP_SHOWWINDOW); 62var 63 TabControl: TCustomTabControl; 64 PageIndex: Integer; 65 PageHandle: HWND; 66begin 67 TabControl := GetWin32WindowInfo(TabControlHandle)^.WinControl as TCustomTabControl; 68 PageIndex := Windows.SendMessage(TabControlHandle, TCM_GETCURSEL, 0, 0); 69 PageIndex := TabControl.TabToPageIndex(PageIndex); 70 71 if (TabControl is TTabControl) then 72 exit(PageIndex); 73 74 if PageIndex = -1 then 75 exit(PageIndex); //DONE: must return something! 76 77 PageHandle := TabControl.CustomPage(PageIndex).Handle; 78 Windows.SetWindowPos(PageHandle, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE or SWP_NOSIZE or SWP_NOACTIVATE or ShowFlags[Showing]); 79 Windows.RedrawWindow(PageHandle, nil, 0, RDW_INVALIDATE or RDW_ALLCHILDREN or RDW_ERASE); 80 Result := PageIndex; 81end; 82 83function PageWindowProc(Window: HWnd; Msg: UInt; WParam: Windows.WParam; 84 LParam: Windows.LParam): LResult; stdcall; 85var 86 Info: PWin32WindowInfo; 87begin 88 case Msg of 89 WM_THEMECHANGED: 90 begin 91 ThemeServices.UpdateThemes; 92 TWin32WSCustomPage.ThemeChange(Window); 93 end; 94 WM_SIZE: 95 begin 96 Info := GetWin32WindowInfo(Window); 97 if (Info^.WinControl.Parent is TCustomTabControl) then 98 begin 99 // the TCustomPage size is the ClientRect size of the parent 100 // => invalidate the Parent.ClientRect 101 Info^.WinControl.Parent.InvalidateClientRectCache(false); 102 end; 103 end; 104 end; 105 Result := WindowProc(Window, Msg, WParam, LParam); 106end; 107 108{ TWin32WSCustomPage } 109 110class function TWin32WSCustomPage.CreateHandle(const AWinControl: TWinControl; 111 const AParams: TCreateParams): HWND; 112var 113 Params: TCreateWindowExParams; 114begin 115 // general initialization of Params 116 PrepareCreateWindow(AWinControl, AParams, Params); 117 // customization of Params 118 with Params do 119 begin 120 pClassName := @ClsName[0]; 121 SubClassWndProc := @PageWindowProc; 122 end; 123 // create window 124 FinishCreateWindow(AWinControl, Params, false); 125 // return window handle 126 Result := Params.Window; 127 ThemeChange(Result); 128end; 129 130class procedure TWin32WSCustomPage.DestroyHandle(const AWinControl: TWinControl); 131var 132 PageIndex, RealIndex: integer; 133begin 134 // remove tab from pagecontrol only if not pfRemoving is set 135 // if pfRemoving is set then Tab has been deleted by RemovePage 136 if (AWinControl.Parent <> nil) and (AWinControl.Parent.HandleAllocated) and 137 not (pfRemoving in TCustomPageAccess(AWinControl).Flags) then 138 begin 139 PageIndex := TCustomPage(AWinControl).PageIndex; 140 RealIndex := TCustomTabControl(AWinControl.Parent).PageToTabIndex(PageIndex); 141 if RealIndex <> -1 then 142 begin 143 TWin32WSCustomTabControl.DeletePage(TCustomTabControl(AWinControl.Parent), RealIndex); 144 AWinControl.Parent.InvalidateClientRectCache(False); 145 end; 146 end; 147 TWSWinControlClass(ClassParent).DestroyHandle(AWinControl); 148end; 149 150class procedure TWin32WSCustomPage.ThemeChange(Wnd: HWND); 151var 152 WindowInfo: PWin32WindowInfo; 153begin 154 WindowInfo := GetWin32WindowInfo(Wnd); 155 if WindowInfo <> nil then 156 begin 157 with WindowInfo^ do 158 begin 159 needParentPaint := ThemeServices.ThemesEnabled; 160 isTabPage := ThemeServices.ThemesEnabled; 161 end; 162 end; 163end; 164 165class procedure TWin32WSCustomPage.SetText(const AWinControl: TWinControl; const AText: string); 166var 167 TCI: TC_ITEM; 168 TabControl: TCustomTabControl; 169 PageIndex, RealIndex: integer; 170 TabControlHandle: HWND; 171begin 172 TabControl := TCustomTabControl(AWinControl.Parent); 173 PageIndex := TCustomPage(AWinControl).PageIndex; 174 RealIndex := TabControl.PageToTabIndex(PageIndex); 175 TabControlHandle := TabControl.Handle; 176 // We can't set label of a page not yet added, 177 // Check for valid page index 178 if (RealIndex >= 0) and (RealIndex < Windows.SendMessage(TabControlHandle, TCM_GETITEMCOUNT, 0, 0)) then 179 begin 180 // retrieve page handle from tab as extra check (in case page isn't added yet). 181 TCI.mask := TCIF_PARAM; 182 Windows.SendMessage(TabControlHandle, TCM_GETITEM, RealIndex, LPARAM(@TCI)); 183 if PtrUInt(TCI.lParam) = PtrUInt(AWinControl) then 184 begin 185 TCI.mask := TCIF_TEXT; 186 TCI.pszText := PChar(PWideChar(UTF8ToUTF16(AText))); 187 Windows.SendMessage(TabControlHandle, TCM_SETITEMW, RealIndex, LPARAM(@TCI)); 188 LCLControlSizeNeedsUpdate(TabControl, True); 189 end; 190 end; 191end; 192 193class procedure TWin32WSCustomPage.UpdateProperties(const ACustomPage: TCustomPage); 194var 195 TCI: TC_ITEM; 196 PageIndex, RealIndex: integer; 197 TabControlHandle: HWND; 198begin 199 PageIndex := ACustomPage.PageIndex; 200 RealIndex := TCustomTabControl(ACustomPage.Parent).PageToTabIndex(PageIndex); 201 TabControlHandle := ACustomPage.Parent.Handle; 202 // Check for valid page index 203 if (RealIndex >= 0) and (RealIndex < Windows.SendMessage(TabControlHandle, TCM_GETITEMCOUNT,0,0)) then 204 begin 205 // retrieve page handle from tab as extra check (in case page isn't added yet). 206 TCI.mask := TCIF_PARAM; 207 Windows.SendMessage(TabControlHandle, TCM_GETITEM, RealIndex, LPARAM(@TCI)); 208 if PtrUInt(TCI.lParam) = PtrUInt(ACustomPage) then 209 begin 210 TCI.mask := TCIF_IMAGE; 211 TCI.iImage := TCustomTabControl(ACustomPage.Parent).GetImageIndex(PageIndex); 212 213 Windows.SendMessage(TabControlHandle, TCM_SETITEM, RealIndex, LPARAM(@TCI)); 214 end; 215 end; 216end; 217 218{ TWin32WSCustomTabControl } 219 220function TabControlParentMsgHandler(const AWinControl: TWinControl; Window: HWnd; 221 Msg: UInt; WParam: Windows.WParam; LParam: Windows.LParam; 222 var MsgResult: Windows.LResult; var WinProcess: Boolean): Boolean; 223var 224 NMHdr: PNMHDR; 225 LMNotify: TLMNotify; 226begin 227 Result := False; 228 if Msg = WM_NOTIFY then 229 begin 230 NMHdr := PNMHDR(LParam); 231 with NMHdr^ do 232 case code of 233 TCN_SELCHANGE: 234 begin 235 Result := True; 236 idFrom := ShowHideTabPage(HWndFrom, True); 237 with LMNotify Do 238 begin 239 Msg := LM_NOTIFY; 240 IDCtrl := WParam; 241 NMHdr := PNMHDR(LParam); 242 Result := CallDefaultWindowProc(Window, Msg, WParam, LParam); 243 end; 244 DeliverMessage(AWinControl, LMNotify); 245 TabControlFocusNewControl(AWinControl as TCustomTabControl, idFrom); 246 MsgResult := LMNotify.Result; 247 end; 248 TCN_SELCHANGING: 249 begin 250 Result := True; 251 with LMNotify Do 252 begin 253 Msg := LM_NOTIFY; 254 IDCtrl := WParam; 255 NMHdr := PNMHDR(LParam); 256 Result := CallDefaultWindowProc(Window, Msg, WParam, LParam); 257 end; 258 DeliverMessage(AWinControl, LMNotify); 259 if LMNotify.Result = 0 then 260 ShowHideTabPage(HWndFrom, False); 261 MsgResult := LMNotify.Result; 262 end; 263 end; 264 end; 265end; 266 267procedure TabControlClientOffset(const AWinControl: TWinControl; 268 var ORect: Rect); 269var 270 ARect: TRect; 271begin 272 // Can't use complete client rect in win32 interface, top part contains the tabs 273 Windows.GetClientRect(AWinControl.Handle, @ARect); 274 ORect := ARect; 275 Windows.SendMessage(AWinControl.Handle, TCM_AdjustRect, 0, LPARAM(@ORect)); 276 Dec(ORect.Right, ARect.Right); 277 Dec(ORect.Bottom, ARect.Bottom); 278end; 279 280class procedure TWin32WSCustomTabControl.DeletePage( 281 const ATabControl: TCustomTabControl; const AIndex: integer); 282 283var 284 Wnd: HWND; 285 286 function TabsScrollingNeeded: Boolean; 287 var 288 HitTestInfo: TC_HITTESTINFO; 289 ARect: TRect; 290 TabCount, FirstShowedIndex: Integer; 291 begin 292 if AIndex <= 0 then Exit(False); 293 294 TabCount := Windows.SendMessage(Wnd, TCM_GETITEMCOUNT, 0, 0); 295 if AIndex < TabCount - 1 then Exit(False); 296 297 // we have to look, if the first shown tab is the tab that is to be deleted 298 Windows.GetClientRect(Wnd, @ARect); 299 Windows.SendMessage(Wnd, TCM_AdjustRect, 0, LPARAM(@ARect)); 300 301 HitTestInfo.pt.x := ARect.Left; 302 HitTestInfo.pt.y := ARect.Top div 2; 303 FirstShowedIndex := Windows.SendMessage(Wnd, TCM_HITTEST, 0, LPARAM(@HitTestInfo)); 304 305 Result := (FirstShowedIndex > 0) and (FirstShowedIndex = AIndex); 306 end; 307 308begin 309// There is a bug in Windows. When only one tab is left in a scrolled Tab Control 310// and this is deleted, Windows doesn't scroll it automatically. So we have to 311// do it manually. See Mantis #19278 312 Wnd := ATabControl.Handle; 313 if TabsScrollingNeeded then 314 Windows.SendMessage(Wnd, TCM_SETCURSEL, Windows.WPARAM(AIndex - 1), 0); 315 Windows.SendMessage(Wnd, TCM_DELETEITEM, Windows.WPARAM(AIndex), 0); 316end; 317 318function CustomTabControlWndProc(Window: HWnd; Msg: UInt; WParam: Windows.WParam; 319 LParam: Windows.LParam): LResult; stdcall; 320var 321 Control: TWinControl; 322 LMessage: TLMessage; 323begin 324 case Msg of 325 WM_ERASEBKGND: 326 begin 327 if not ThemeServices.ThemesEnabled then 328 begin 329 // prevent flickering 330 Control := GetWin32WindowInfo(Window)^.WinControl; 331 LMessage.msg := Msg; 332 LMessage.wParam := WParam; 333 LMessage.lParam := LParam; 334 LMessage.Result := 0; 335 Result := DeliverMessage(Control, LMessage); 336 Exit; 337 end; 338 end; 339 end; 340 Result := WindowProc(Window, Msg, WParam, LParam); 341end; 342 343class function TWin32WSCustomTabControl.CreateHandle(const AWinControl: TWinControl; 344 const AParams: TCreateParams): HWND; 345const 346 TabPositionFlags: array[TTabPosition, Boolean] of DWord = ( 347 { tpTop } (0, 0), 348 { tpBottom } (TCS_BOTTOM, TCS_BOTTOM), 349 { tpLeft } (TCS_MULTILINE or TCS_VERTICAL, TCS_MULTILINE or TCS_VERTICAL or TCS_RIGHT), 350 { tpRight } (TCS_MULTILINE or TCS_VERTICAL or TCS_RIGHT, TCS_MULTILINE or TCS_VERTICAL) 351 ); 352 TabStyleFlags: array[TTabStyle] of DWord = ( 353 { tsTabs } TCS_TABS, 354 { tsButtons } TCS_BUTTONS, 355 { tsFlatButtons } TCS_BUTTONS or TCS_FLATBUTTONS 356 ); 357var 358 Params: TCreateWindowExParams; 359 T: TCustomTabControl; 360begin 361 T := TCustomTabControl(AWinControl); 362 // general initialization of Params 363 PrepareCreateWindow(AWinControl, AParams, Params); 364 // customization of Params 365 with Params do 366 begin 367 SubClassWndProc := @CustomTabControlWndProc; 368 if AWinControl is TTabControl then begin 369 // TTabControl is not really a TabControl, it is a container, and has a child that does the tabs 370 pClassName := @ClsName[0]; 371 end 372 else begin 373 Flags := Flags or TabPositionFlags[T.TabPosition, T.UseRightToLeftAlignment]; 374 Flags := Flags or TabStyleFlags[T.Style]; 375 if not T.TabStop then 376 Flags := Flags or TCS_FOCUSNEVER; 377 if nboMultiLine in T.Options then 378 Flags := Flags or TCS_MULTILINE; 379 if T.MultiSelect then 380 Flags := Flags or TCS_MULTISELECT; 381 if T.RaggedRight then 382 Flags := Flags or TCS_RAGGEDRIGHT; 383 if T.ScrollOpposite then 384 Flags := Flags or TCS_SCROLLOPPOSITE; 385 if T.TabWidth > 0 then 386 Flags := Flags or TCS_FIXEDWIDTH; 387 if T.HotTrack and not (csDesigning in T.ComponentState) then 388 Flags := Flags or TCS_HOTTRACK; 389 if T.OwnerDraw and not (csDesigning in T.ComponentState) then 390 Flags := Flags or TCS_OWNERDRAWFIXED; 391 pClassName := WC_TABCONTROL; 392 end; 393 end; 394 // create window 395 FinishCreateWindow(AWinControl, Params, false); 396 Result := Params.Window; 397 398 if not (AWinControl is TTabControl) then begin 399 if T.Images <> nil then 400 SendMessage(Result, TCM_SETIMAGELIST, 0, T.Images.ReferenceForPPI[T.ImagesWidth, T.Font.PixelsPerInch]._Handle); 401 402 // although we may be child of tabpage, cut the paint chain 403 // to improve speed and possible paint anomalities 404 Params.WindowInfo^.ParentMsgHandler := @TabControlParentMsgHandler; 405 Params.WindowInfo^.needParentPaint := false; 406 Params.WindowInfo^.ClientOffsetProc := @TabControlClientOffset; 407 408 SendMessage(Result, TCM_SETITEMSIZE, 0, MakeLParam( 409 T.TabWidth, 410 T.TabHeight)); 411 end; 412end; 413 414class procedure TWin32WSCustomTabControl.AddPage(const ATabControl: TCustomTabControl; 415 const AChild: TCustomPage; const AIndex: integer); 416var 417 TCI: TC_ITEM; 418begin 419 if ATabControl is TTabControl then 420 exit; 421 422 with ATabControl do 423 begin 424 // other widgetsets allocates handles because they really need this 425 // but on windows page handle is differ from tab and thus allocation can be 426 // postponed, but this cause problems with event handling like bug #0012434 427 // so to overcome such problems we need to allocate this handle 428 if not (ATabControl is TTabControl) then 429 AChild.HandleNeeded; 430 if ShowTabs then 431 begin 432 TCI.Mask := TCIF_TEXT or TCIF_PARAM or TCIF_IMAGE; 433 // store object as extra, so we can verify we got the right page later 434 TCI.lParam := PtrInt(AChild); 435 TCI.iImage := ATabControl.GetImageIndex(ATabControl.TabToPageIndex(AIndex)); 436 TCI.pszText := PChar(PWideChar(UTF8ToUTF16(AChild.Caption))); 437 Windows.SendMessage(Handle, TCM_INSERTITEMW, AIndex, LPARAM(@TCI)); 438 end; 439 // clientrect possible changed, adding first tab, or deleting last 440 // windows should send a WM_SIZE message because of this, but it doesn't 441 // send it ourselves 442 if LCLControlSizeNeedsUpdate(ATabControl, True) then 443 AdjustSizeTabControlPages(ATabControl); 444 end; 445end; 446 447class procedure TWin32WSCustomTabControl.MovePage(const ATabControl: TCustomTabControl; 448 const AChild: TCustomPage; const NewIndex: integer); 449var 450 Index: Integer; 451 TCI: TC_ITEM; 452begin 453 if ATabControl is TTabControl then 454 exit; 455 456 if not ATabControl.ShowTabs then 457 Exit; 458 459 Index := AChild.VisibleIndex; 460 TCI.Mask := TCIF_IMAGE or TCIF_PARAM; 461 Windows.SendMessage(ATabControl.Handle, TCM_GETITEMW, Windows.WPARAM(Index), LParam(@TCI)); 462 Windows.SendMessage(ATabControl.Handle, TCM_DELETEITEM, Windows.WPARAM(Index), 0); 463 TCI.Mask := TCI.Mask or TCIF_TEXT; 464 TCI.pszText := PChar(PWideChar(UTF8ToUTF16(AChild.Caption))); 465 Windows.SendMessage(ATabControl.Handle, TCM_INSERTITEMW, NewIndex, LPARAM(@TCI)); 466 if LCLControlSizeNeedsUpdate(ATabControl, True) then 467 AdjustSizeTabControlPages(ATabControl); 468end; 469 470class procedure TWin32WSCustomTabControl.RemovePage(const ATabControl: TCustomTabControl; 471 const AIndex: integer); 472begin 473 if ATabControl is TTabControl then 474 exit; 475 476 DeletePage(ATabControl, AIndex); 477 if LCLControlSizeNeedsUpdate(ATabControl, True) then 478 AdjustSizeTabControlPages(ATabControl); 479end; 480 481class function TWin32WSCustomTabControl.GetNotebookMinTabHeight(const AWinControl: TWinControl): integer; 482var 483 R: TRect; 484begin 485 if AWinControl is TTabControl then begin 486 Result := 0;; 487 exit; 488 end; 489 490 if (not GetLCLClientBoundsOffset(AWinControl, R)) then begin 491 Result := inherited GetNotebookMinTabHeight(AWinControl); 492 exit; 493 end; 494 // The bigger offset is the height of the tab 495 Result := Max(R.Top, -R.Bottom) 496 // but includes spacing for the child 497 - 1; 498end; 499 500class function TWin32WSCustomTabControl.GetNotebookMinTabWidth(const AWinControl: TWinControl): integer; 501var 502 R: TRect; 503begin 504 if AWinControl is TTabControl then begin 505 Result := 0;; 506 exit; 507 end; 508 509 if (not GetLCLClientBoundsOffset(AWinControl, R)) then begin 510 Result := inherited GetNotebookMinTabHeight(AWinControl); 511 exit; 512 end; 513 Result := Max(R.Left, -R.Right) 514 - 1; 515end; 516 517{ ----------------------------------------------------------------------------- 518 Method: AddAllNBPages 519 Adds all pages to tab control (showtabs becomes true) 520 ------------------------------------------------------------------------------} 521class procedure TWin32WSCustomTabControl.AddAllNBPages(const ATabControl: TCustomTabControl); 522var 523 TCI: TC_ITEM; 524 I, Res, RealIndex: Integer; 525 APage: TCustomPage; 526 WinHandle: HWND; 527begin 528 if ATabControl is TTabControl then 529 exit; 530 531 WinHandle := ATabControl.Handle; 532 RealIndex := 0; 533 for I := 0 to ATabControl.PageCount - 1 do 534 begin 535 APage := ATabControl.Page[I]; 536 if not APage.TabVisible and not (csDesigning in APage.ComponentState) then 537 continue; 538 // check if already shown 539 TCI.Mask := TCIF_PARAM; 540 Res := Windows.SendMessage(ATabControl.Handle, TCM_GETITEM, RealIndex, LPARAM(@TCI)); 541 if (Res = 0) or (PtrUInt(TCI.lParam) <> PtrUInt(APage)) then 542 begin 543 TCI.Mask := TCIF_TEXT or TCIF_PARAM or TCIF_IMAGE; 544 TCI.lParam := PtrUInt(APage); 545 TCI.iImage := ATabControl.GetImageIndex(I); 546 TCI.pszText := PChar(PWideChar(UTF8ToUTF16(APage.Caption))); 547 Windows.SendMessage(WinHandle, TCM_INSERTITEMW, RealIndex, LPARAM(@TCI)); 548 end; 549 Inc(RealIndex); 550 end; 551 SetPageIndex(ATabControl, ATabControl.PageIndex); // index may not have been updated while pages where hidden 552 AdjustSizeTabControlPages(ATabControl); 553end; 554 555class procedure TWin32WSCustomTabControl.AdjustSizeTabControlPages(const ATabControl: TCustomTabControl); 556var 557 I: Integer; 558 R: TRect; 559 WinHandle: HWND; 560 lPage: TCustomPage; 561begin 562 if ATabControl is TTabControl then 563 exit; 564 565 WinHandle := ATabControl.Handle; 566 // Adjust page size to fit in tabcontrol, need bounds of tab control in client of parent 567 TWin32WidgetSet(WidgetSet).GetClientRect(WinHandle, R); 568 for I := 0 to ATabControl.PageCount - 1 do 569 begin 570 lPage := ATabControl.Page[I]; 571 // we don't need to resize non-existing pages yet, they will be sized when created 572 if lPage.HandleAllocated then 573 SetBounds(lPage, R.Left, R.Top, R.Right, R.Bottom); 574 end; 575end; 576 577{------------------------------------------------------------------------------ 578 Method: RemoveAllNBPages 579 580 Removes all pages from a tab control (showtabs becomes false) 581 ------------------------------------------------------------------------------} 582class procedure TWin32WSCustomTabControl.RemoveAllNBPages(const ATabControl: TCustomTabControl); 583var 584 I: Integer; 585 WinHandle: HWND; 586begin 587 if ATabControl is TTabControl then 588 exit; 589 590 WinHandle := ATabControl.Handle; 591 for I := ATabControl.PageCount - 1 downto 0 do 592 Windows.SendMessage(WinHandle, TCM_DELETEITEM, Windows.WPARAM(I), 0); 593 AdjustSizeTabControlPages(ATabControl); 594end; 595 596procedure SendSelChangeMessage(const ATabControl: TCustomTabControl; const AHandle: HWND; 597 const APageIndex: integer); 598var 599 Mess: TLMNotify; 600 NMHdr: tagNMHDR; 601begin 602 FillChar(Mess,SizeOf(Mess),0); 603 Mess.Msg := LM_NOTIFY; 604 FillChar(NMHdr,SizeOf(NMHdr),0); 605 NMHdr.code := TCN_SELCHANGE; 606 NMHdr.hwndfrom := AHandle; 607 NMHdr.idfrom := APageIndex; //use this to set pageindex to the correct page. 608 Mess.NMHdr := @NMHdr; 609 DeliverMessage(ATabControl, TLMessage(Mess)); 610end; 611 612class function TWin32WSCustomTabControl.GetTabIndexAtPos(const ATabControl: TCustomTabControl; 613 const AClientPos: TPoint): integer; 614var 615 hittestInfo: TC_HITTESTINFO; 616 Orect: TRect; 617begin 618 if ATabControl is TTabControl then begin 619 Result := 0;; 620 exit; 621 end; 622 623 GetLCLClientBoundsOffset(ATabControl, ORect); 624 hittestInfo.pt.x := AClientPos.x + ORect.Left; 625 hittestInfo.pt.y := AClientPos.y + ORect.Top; 626 Result := Windows.SendMessage(ATabControl.Handle, TCM_HITTEST, 0, LPARAM(@hittestInfo)); 627end; 628 629class function TWin32WSCustomTabControl.GetTabRect(const ATabControl: TCustomTabControl; 630 const AIndex: Integer): TRect; 631var 632 Orect: TRect; 633begin 634 if ATabControl is TTabControl then begin 635 Result.Top := 0;; 636 Result.Left := 0;; 637 Result.Bottom := 0;; 638 Result.Right := 0;; 639 exit; 640 end; 641 642 GetLCLClientBoundsOffset(ATabControl, ORect); 643 if Windows.SendMessage(ATabControl.Handle, TCM_GETITEMRECT, WPARAM(AIndex), LPARAM(@Result)) <> 0 644 then begin 645 Result.Top := Result.Top - Orect.Top; 646 Result.Bottom := Result.Bottom - Orect.Top; 647 Result.Left := Result.Left - Orect.Left; 648 Result.Right := Result.Right - Orect.Left; 649 end 650 else 651 Result := inherited GetTabRect(ATabControl, AIndex); 652end; 653 654class function TWin32WSCustomTabControl.GetCapabilities: TCTabControlCapabilities; 655begin 656 Result:=[nbcMultiLine, nbcTabsSizeable]; 657end; 658 659class function TWin32WSCustomTabControl.GetDesignInteractive( 660 const AWinControl: TWinControl; AClientPos: TPoint): Boolean; 661var 662 hittestInfo: TC_HITTESTINFO; 663 AIndex, ACurIndex: Integer; 664begin 665 if AWinControl is TTabControl then begin 666 Result := inherited GetDesignInteractive(AWinControl, AClientPos); 667 exit; 668 end; 669 670 hittestInfo.pt.x := AClientPos.x; 671 hittestInfo.pt.y := AClientPos.y; 672 AIndex := Windows.SendMessage(AWinControl.Handle, TCM_HITTEST, 0, LPARAM(@hittestInfo)); 673 ACurIndex := SendMessage(AWinControl.Handle, TCM_GETCURSEL, 0, 0); 674 Result := (AIndex <> -1) and (AIndex <> ACurIndex); 675end; 676 677class procedure TWin32WSCustomTabControl.SetTabSize( 678 const ATabControl: TCustomTabControl; 679 const ATabWidth, ATabHeight: integer); 680begin 681 if ATabControl is TTabControl then 682 exit; 683 684 Windows.SendMessage(ATabControl.Handle, TCM_SETITEMSIZE, 685 0, MakeLParam(ATabWidth, ATabHeight)); 686end; 687 688class procedure TWin32WSCustomTabControl.SetImageList( 689 const ATabControl: TCustomTabControl; const AImageList: TCustomImageListResolution); 690begin 691 if ATabControl is TTabControl then 692 exit; 693 694 if not WSCheckHandleAllocated(ATabControl, 'SetImageList') then 695 Exit; 696 697 if AImageList <> nil then 698 SendMessage(ATabControl.Handle, TCM_SETIMAGELIST, 0, AImageList.Reference._Handle) 699 else 700 SendMessage(ATabControl.Handle, TCM_SETIMAGELIST, 0, 0); 701 // if you set big images like 32x32 then tabs will be big too => you need to 702 // readjust the size of pages 703 AdjustSizeTabControlPages(ATabControl); 704end; 705 706class procedure TWin32WSCustomTabControl.SetPageIndex(const ATabControl: TCustomTabControl; const AIndex: integer); 707var 708 TabControlHandle, OldPageHandle, NewPageHandle: HWND; 709 NewRealIndex: Integer; 710begin 711 if ATabControl is TTabControl then 712 exit; 713 714 TabControlHandle := ATabControl.Handle; 715 // get the current top window 716 OldPageHandle := GetTopWindow(TabControlHandle); 717 NewPageHandle := 0; 718 NewRealIndex := ATabControl.PageToTabIndex(AIndex); 719 720 SendMessage(TabControlHandle, TCM_SETCURSEL, Windows.WParam(NewRealIndex), 0); 721 722 if (ATabControl is TTabControl) then 723 exit; //all done 724 725 if not (csDestroying in ATabControl.ComponentState) then 726 begin 727 // create handle if not already done, need to show! 728 if (AIndex >= 0) and (AIndex < ATabControl.PageCount) then 729 begin 730 NewPageHandle := ATabControl.Page[AIndex].Handle; 731 Windows.SetWindowPos(NewPageHandle, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE or SWP_NOSIZE or SWP_SHOWWINDOW or SWP_NOACTIVATE); 732 SendSelChangeMessage(ATabControl, TabControlHandle, AIndex); 733 TabControlFocusNewControl(ATabControl, AIndex); 734 end; 735 // traverse children and hide them if needed 736 while OldPageHandle <> 0 do 737 begin 738 // don't touch non-lcl windows 739 if (OldPageHandle <> NewPageHandle) and IsWindowVisible(OldPageHandle) and Assigned(LCLIntf.GetProp(OldPageHandle, 'WinControl')) then 740 Windows.SetWindowPos(OldPageHandle, 0, 0, 0, 0, 0, SWP_NOMOVE or SWP_NOSIZE or SWP_NOZORDER or SWP_HIDEWINDOW or SWP_NOACTIVATE); 741 OldPageHandle := GetNextWindow(OldPageHandle, GW_HWNDNEXT); 742 end; 743 end; 744end; 745 746class procedure TWin32WSCustomTabControl.SetTabPosition(const ATabControl: TCustomTabControl; const ATabPosition: TTabPosition); 747begin 748 if ATabControl is TTabControl then 749 exit; 750 751 if ATabControl.HandleAllocated then 752 RecreateWnd(ATabControl); 753end; 754 755class procedure TWin32WSCustomTabControl.ShowTabs(const ATabControl: TCustomTabControl; AShowTabs: boolean); 756begin 757 if ATabControl is TTabControl then 758 exit; 759 760 if AShowTabs then 761 AddAllNBPages(ATabControl) 762 else 763 RemoveAllNBPages(ATabControl); 764end; 765 766class procedure TWin32WSCustomTabControl.UpdateProperties(const ATabControl: TCustomTabControl); 767var 768 CurrentStyle, NewStyle: cardinal; 769begin 770 if ATabControl is TTabControl then 771 exit; 772 773 CurrentStyle := GetWindowLong(ATabControl.Handle, GWL_STYLE); 774 if (nboMultiLine in ATabControl.Options) or (ATabControl.TabPosition in [tpLeft, tpRight]) then 775 NewStyle := CurrentStyle or TCS_MULTILINE 776 else 777 NewStyle := CurrentStyle and not TCS_MULTILINE; 778 if NewStyle <> CurrentStyle then 779 begin 780 SetWindowLong(ATabControl.Handle, GWL_STYLE, NewStyle); 781 SetWindowPos(ATabControl.Handle, 0, 0, 0, 0, 0, SWP_NOSIZE or SWP_NOMOVE or SWP_NOZORDER or SWP_DRAWFRAME); 782 if LCLControlSizeNeedsUpdate(ATabControl, True) then 783 AdjustSizeTabControlPages(ATabControl); 784 end; 785end; 786 787