{%MainUnit ../comctrls.pp} { TToolButton ***************************************************************************** This file is part of the Lazarus Component Library (LCL) See the file COPYING.modifiedLGPL.txt, included in this distribution, for details about the license. ***************************************************************************** } { TToolButtonActionLink } procedure TToolButtonActionLink.AssignClient(AClient: TObject); begin inherited AssignClient(AClient); FClient := AClient as TToolButton; end; function TToolButtonActionLink.IsCheckedLinked: Boolean; begin Result := inherited IsCheckedLinked and (TToolButton(FClient).Down = (Action as TCustomAction).Checked); end; function TToolButtonActionLink.IsImageIndexLinked: Boolean; begin Result := inherited IsImageIndexLinked and (TToolButton(FClient).ImageIndex = (Action as TCustomAction).ImageIndex); end; procedure TToolButtonActionLink.SetChecked(Value: Boolean); begin if IsCheckedLinked then TToolButton(FClient).Down := Value; end; procedure TToolButtonActionLink.SetImageIndex(Value: Integer); begin if IsImageIndexLinked then TToolButton(FClient).ImageIndex := Value; end; { TToolButton } constructor TToolButton.Create(TheOwner: TComponent); begin inherited Create(TheOwner); FImageIndex := -1; FStyle := tbsButton; FShowCaption := true; ControlStyle := [csCaptureMouse, csSetCaption, csDesignNoSmoothResize]; with GetControlClassDefaultSize do SetInitialBounds(0, 0, CX, CY); AccessibleRole := larToolBarButton; end; procedure TToolButton.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); procedure SendButtonUpMsg; var msg: TLMMouse; pt: TPoint; begin FillChar({%H-}msg, SizeOf(msg), 0); msg.Msg:=LM_LBUTTONUP; pt := ScreenToClient(Mouse.CursorPos); msg.XPos:=pt.X; msg.YPos:=pt.Y; WndProc(TLMessage(msg)); end; var NewFlags: TToolButtonFlags; APointInArrow: Boolean; begin //debugln(['TToolButton.MouseDown ',DbgSName(Self)]); SetMouseInControl(True); NewFlags := FToolButtonFlags - [tbfPressed, tbfArrowPressed]; if (Button = mbLeft) then begin APointInArrow := PointInArrow(X, Y); //use some threshold to decide if the DropdownMenu should be opened again. // When no DropdownMenu is assigned, FLastDropDownTick is always 0 // therefore the condition is always met. if Enabled and not( (GetTickCount64 < FLastDropDownTick + 100) and (APointInArrow or (Style<>tbsDropDown))) then begin if APointInArrow then Include(NewFlags, tbfArrowPressed) else Include(NewFlags, tbfPressed); end; if NewFlags <> FToolButtonFlags then begin FToolButtonFlags := NewFlags; Invalidate; end; end; FLastDown := Down; inherited MouseDown(Button, Shift, X, Y); FLastDropDownTick := 0; if (Button = mbLeft) and Enabled and (Style in [tbsButton, tbsDropDown, tbsButtonDrop]) then begin if ((Style in [tbsButton, tbsButtonDrop]) and (tbfPressed in NewFlags) or (Style = tbsDropDown) and (tbfArrowPressed in NewFlags)) and CheckMenuDropdown then begin FLastDropDownTick := GetTickCount64; //because we show the DropdownMenu in MouseDown, we have to send // LM_LBUTTONUP manually to make it work in all widgetsets! // Some widgetsets work without it (e.g. win32) but some don't (e.g. carbon). SendButtonUpMsg; end else begin if (Style = tbsDropDown) and (NewFlags * [tbfArrowPressed, tbfPressed] = [tbfPressed]) then Down := True; end; end; end; procedure TToolButton.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var ButtonPressed, ArrowPressed: Boolean; Pt: TPoint; NewFlags: TToolButtonFlags; begin //DebugLn(['TToolButton.MouseUp ',Name,':',ClassName,' ',dbgs(ord(Button)),' ',X,',',Y]); FLastDown := False; NewFlags := FToolButtonFlags; ButtonPressed := (Button = mbLeft) and (tbfPressed in NewFlags); ArrowPressed := (Button = mbLeft) and (tbfArrowPressed in NewFlags); if ButtonPressed then Exclude(NewFlags, tbfPressed); if ArrowPressed then Exclude(NewFlags, tbfArrowPressed); if (tbfMouseInArrow in NewFlags) and PointInArrow(X, Y) then Exclude(NewFlags, tbfMouseInArrow); if NewFlags <> FToolButtonFlags then begin FToolButtonFlags := NewFlags; Invalidate; end; inherited MouseUp(Button, Shift, X, Y); if (Button = mbLeft) then begin if FMouseInControl then begin Pt := Point(X, Y); if not PtInRect(Rect(0,0,Width,Height), Pt) then SetMouseInControl(false); end; if (Style in [tbsButton, tbsDropDown, tbsButtonDrop]) then Down := False; //button is pressed, but DropdownMenu was not shown if FMouseInControl and (FLastDropDownTick = 0) then begin if ButtonPressed then begin if (Style = tbsCheck) then Down := not Down; Click; end else if ArrowPressed then ArrowClick; //DON'T USE the tool button (Self) after the click call because it could //have been destroyed in the OnClick event handler (e.g. Lazarus IDE does it)! end; end; end; procedure TToolButton.Notification(AComponent: TComponent; Operation: TOperation); begin inherited Notification(AComponent, Operation); if Operation = opRemove then begin if AComponent = DropdownMenu then DropdownMenu := nil else if AComponent = MenuItem then MenuItem := nil; end; end; procedure TToolButton.Paint; procedure DrawDropDownArrow(OwnerDetails: TThemedElementDetails; const DropDownButtonRect: TRect); var Details: TThemedElementDetails; ArrowState: TThemedToolBar; begin if Style = tbsButtonDrop then begin if Enabled then ArrowState := ttbSplitButtonDropDownNormal else ArrowState := ttbSplitButtonDropDownDisabled; end else begin ArrowState := TThemedToolBar(ord(ttbSplitButtonDropDownNormal) + OwnerDetails.State - 1); if (tbfArrowPressed in FToolButtonFlags) and FMouseInControl and Enabled then ArrowState := ttbSplitButtonDropDownPressed else if (FToolButtonFlags*[tbfMouseInArrow,tbfPressed] = [tbfPressed]) and not FLastDown then ArrowState := ttbSplitButtonDropDownHot; end; Details := ThemeServices.GetElementDetails(ArrowState); if (FToolBar <> nil) and (not FToolBar.Flat) and (Style <> tbsButtonDrop) and (Details.State in [1, 4]) then Details.State := 2; ThemeServices.DrawElement(Canvas.Handle, Details, DropDownButtonRect); end; procedure DrawDivider(Details: TThemedElementDetails; ARect: TRect); begin // theme services have no strict rule to draw divider in the center, // so we should calculate rectangle here // on windows 7 divider can't be less than 4 pixels if FToolBar.IsVertical then begin if (ARect.Bottom - ARect.Top) > 5 then begin ARect.Top := (ARect.Top + ARect.Bottom) div 2 - 3; ARect.Bottom := ARect.Top + 5; end; end else begin if (ARect.Right - ARect.Left) > 5 then begin ARect.Left := (ARect.Left + ARect.Right) div 2 - 3; ARect.Right := ARect.Left + 5; end; end; ThemeServices.DrawElement(Canvas.GetUpdatedHandle([csBrushValid, csPenValid]), Details, ARect); end; procedure DrawSeparator(Details: TThemedElementDetails; ARect: TRect); begin // separator is just an empty space between buttons, so we should not draw anything, // but vcl draws line when toolbar is flat, because there is no way to detect // space between flat buttons. Better if we draw something too. One of suggestions // was to draw 2 lines instead of one divider - this way separator and divider will differ if FToolBar.Flat then // draw it only for flat Toolbar begin if FToolBar.IsVertical then begin if (ARect.Bottom - ARect.Top) >= 10 then begin ARect.Top := (ARect.Top + ARect.Bottom) div 2 - 5; ARect.Bottom := ARect.Top + 5; DrawDivider(Details, ARect); Types.OffsetRect(ARect, 0, 5); DrawDivider(Details, ARect); end else DrawDivider(Details, ARect); end else begin if (ARect.Right - ARect.Left) >= 10 then begin ARect.Left := (ARect.Left + ARect.Right) div 2 - 5; ARect.Right := ARect.Left + 5; DrawDivider(Details, ARect); Types.OffsetRect(ARect, 5, 0); DrawDivider(Details, ARect); end else DrawDivider(Details, ARect); end; end; end; var PaintRect: TRect; ButtonRect: TRect; MainBtnRect: TRect; DropDownButtonRect: TRect; TextSize: TSize; TextPos: TPoint; dist, marg: Integer; IconSize: TSize; IconPos: TPoint; ImgList: TCustomImageList; ImgIndex: integer; Details, TempDetails: TThemedElementDetails; ImgEffect: TGraphicsDrawEffect; begin if (FToolBar<>nil) and (ClientWidth>0) and (ClientHeight>0) then begin PaintRect := ClientRect; // the whole paint area // calculate button area(s) MainBtnRect := PaintRect; ButtonRect := PaintRect; Details := GetButtonDrawDetail; // OnDrawItem if Assigned(FToolBar.OnPaintButton) then begin if (Style in [tbsButton, tbsDropDown, tbsButtonDrop, tbsCheck]) then begin TempDetails := Details; if ((FToolBar <> nil) and not FToolBar.Flat) and (TempDetails.State in [1, 4]) then TempDetails.State := 2; end; FToolBar.OnPaintButton(Self, TempDetails.State); exit; end; if Style in [tbsDropDown, tbsButtonDrop] then begin DropDownButtonRect := ButtonRect; if Style = tbsDropDown then DropDownButtonRect.Left := DropDownButtonRect.Right-FToolBar.DropDownWidth else begin DropDownButtonRect.Left := DropDownButtonRect.Right-FToolBar.ButtonDropWidth; DropDownButtonRect.Right := DropDownButtonRect.Left + FToolBar.DropDownWidth; end; MainBtnRect.Right := DropDownButtonRect.Left; if Style = tbsDropDown then ButtonRect := MainBtnRect else Inc(MainBtnRect.Right, cDefButtonDropDecArrowWidth); // tbsButtonDrop ignore extra space between button and arrow end else DropDownButtonRect := Rect(0,0,0,0); // calculate text size TextSize.cx:=0; TextSize.cy:=0; if (Style in [tbsButton, tbsDropDown, tbsButtonDrop, tbsCheck]) and (FToolBar.ShowCaptions) and ((FToolbar.List and ShowCaption) or not FToolBar.List) and //Allow hide caption only in list mode (Caption <> '') then TextSize := GetTextSize; // calculate icon size IconSize := Size(0,0); GetCurrentIcon(ImgList, ImgIndex, ImgEffect); if (ImgList<>nil) then begin IconSize := ImgList.SizeForPPI[FToolBar.ImagesWidth, Font.PixelsPerInch]; if IconSize.cy <= 0 then IconSize.cx := 0; end; // calculate text and icon position TextPos:=Point(0,0); IconPos:=Point(0,0); if TextSize.cx > 0 then begin if IconSize.cx > 0 then begin if FToolBar.List then begin // icon left of text dist := FToolbar.Scale96ToFont(cHorIconTextDist); IconPos.X:=(MainBtnRect.Left+MainBtnRect.Right-IconSize.cx-TextSize.cx-dist) div 2; IconPos.Y:=(MainBtnRect.Top+MainBtnRect.Bottom-IconSize.cy) div 2; TextPos.X:=IconPos.X+IconSize.cx+dist; TextPos.Y:=(MainBtnRect.Top+MainBtnRect.Bottom-TextSize.cy) div 2; end else begin // icon above text dist := cVertIconTextDist; IconPos.X:=(MainBtnRect.Left+MainBtnRect.Right-IconSize.cx) div 2; IconPos.Y:=(MainBtnRect.Top+MainBtnRect.Bottom-IconSize.cy-TextSize.cy-dist) div 2; TextPos.X:=(MainBtnRect.Left+MainBtnRect.Right-TextSize.cx) div 2; TextPos.Y:=IconPos.Y+IconSize.cy+dist; end; end else begin // only text TextPos.X:=(MainBtnRect.Left+MainBtnRect.Right-TextSize.cx) div 2; TextPos.Y:=(MainBtnRect.Top+MainBtnRect.Bottom-TextSize.cy) div 2; end; end else if IconSize.cx>0 then begin // only icon IconPos.X:=(MainBtnRect.Left+MainBtnRect.Right-IconSize.cx) div 2; IconPos.Y:=(MainBtnRect.Top+MainBtnRect.Bottom-IconSize.cy) div 2; end; // draw button if (Style in [tbsButton, tbsDropDown, tbsButtonDrop, tbsCheck]) then begin // non-Flat toolbars come from old windows where you was able to set how // to draw it by adjusting toolbar window options // with current windows toolbars should be drawn using Theme // so let's treat flat toolbars as starndard toolbars and draw them using ThemeManager // and to draw a non-Flat toolbars we need to somehow mimic always raised state // of their buttons - a good way is to draw them using Hot style also for // normal and disables states TempDetails := Details; if ((FToolBar <> nil) and not FToolBar.Flat) and (TempDetails.State in [1, 4]) then TempDetails.State := 2; ThemeServices.DrawElement(Canvas.GetUpdatedHandle([csBrushValid, csPenValid]), TempDetails, ButtonRect); ButtonRect := ThemeServices.ContentRect(Canvas.Handle, TempDetails, ButtonRect); end else if Style = tbsDivider then begin DrawDivider(Details, ButtonRect); ButtonRect := Rect(0, 0, 0, 0); // nothing can be drawn on divider end else if Style = tbsSeparator then begin if ThemeServices.ThemesEnabled then begin Details:=ThemeServices.GetElementDetails(ttbSeparatorNormal); ThemeServices.DrawElement(Canvas.Handle,Details,ClientRect) end else DrawSeparator(Details, ButtonRect); ButtonRect := Rect(0, 0, 0, 0); // nothing can be drawn on separator end; // draw dropdown button if Style in [tbsDropDown, tbsButtonDrop] then DrawDropDownArrow(Details, DropDownButtonRect); // draw icon if (ImgList<>nil) then ImgList.ResolutionForPPI[FToolBar.ImagesWidth, Font.PixelsPerInch, GetCanvasScaleFactor] .Draw(Canvas, IconPos.X, IconPos.Y, ImgIndex, ImgEffect); // draw text if (TextSize.cx > 0) then begin MainBtnRect.Left := TextPos.X; MainBtnRect.Top := TextPos.Y; // if State is disabled then change to PushButtonDisabled since // ToolButtonDisabled text looks not disabled though windows native toolbutton // text drawn with disabled look. For other widgetsets there is no difference which // disabled detail to use TempDetails := Details; if TempDetails.State = 4 then TempDetails := ThemeServices.GetElementDetails(tbPushButtonDisabled); ThemeServices.DrawText(Canvas, TempDetails, Caption, MainBtnRect, DT_LEFT or DT_TOP, 0); end; // draw separator (at runtime: just space, at designtime: a rectangle) if (Style = tbsSeparator) and (csDesigning in ComponentState) then begin Canvas.Brush.Color := clBackground; Canvas.Pen.Color := clBlack; dec(PaintRect.Right); dec(PaintRect.Bottom); Canvas.FrameRect(PaintRect); end; end; inherited Paint; end; function TToolButton.PointInArrow(const X, Y: Integer): Boolean; begin Result := (Style = tbsDropDown) and (FToolBar <> nil) and (Y >= 0) and (Y <= ClientHeight) and (X > ClientWidth - FToolBar.DropDownWidth) and (X <= ClientWidth); end; procedure TToolButton.Loaded; begin inherited Loaded; CopyPropertiesFromMenuItem(FMenuItem); end; procedure TToolButton.SetAutoSize(Value: Boolean); begin if Value = AutoSize then exit; inherited SetAutoSize(Value); RequestAlign; end; procedure TToolButton.RealSetText(const AValue: TCaption); begin if ([csLoading,csDestroying]*ComponentState=[]) then begin InvalidatePreferredSize; GetAccessibleObject.AccessibleName := AValue; inherited RealSetText(AValue); AdjustSize; end else inherited RealSetText(AValue); end; procedure TToolButton.SetToolBar(NewToolBar: TToolBar); begin if FToolBar = NewToolBar then exit; Parent := NewToolBar; end; procedure TToolButton.ActionChange(Sender: TObject; CheckDefaults: Boolean); var NewAction: TCustomAction; begin inherited ActionChange(Sender, CheckDefaults); if Sender is TCustomAction then begin NewAction := TCustomAction(Sender); if (not CheckDefaults) or (not Down) then Down := NewAction.Checked; if (not CheckDefaults) or (ImageIndex<0) then ImageIndex := NewAction.ImageIndex; end; end; procedure TToolButton.ArrowClick; begin if Assigned(FOnArrowClick) then FOnArrowClick(Self); end; function TToolButton.GetActionLinkClass: TControlActionLinkClass; begin Result := TToolButtonActionLink; end; procedure TToolButton.CopyPropertiesFromMenuItem(const Value: TMenuItem); begin if not Assigned(Value) then Exit; BeginUpdate; Action := Value.Action; Caption := Value.Caption; Down := Value.Checked; Enabled := Value.Enabled; Hint := Value.Hint; ImageIndex := Value.ImageIndex; Visible := Value.Visible; EndUpdate; end; procedure TToolButton.CMHitTest(var Message: TCMHitTest); begin if (not (Style in [tbsDivider, tbsSeparator])) or (DragKind = dkDock) then Message.Result := 1 else Message.Result := 0; end; class procedure TToolButton.WSRegisterClass; begin inherited WSRegisterClass; RegisterCustomToolButton; end; procedure TToolButton.MouseEnter; begin // DebugLn('TToolButton.MouseEnter ',Name); inherited MouseEnter; SetMouseInControl(true); end; procedure TToolButton.MouseLeave; begin // DebugLn('TToolButton.MouseLeave ',Name); inherited MouseLeave; if not(tbfDropDownMenuShown in FToolButtonFlags) then begin if (not MouseCapture) and ([tbfPressed, tbfArrowPressed, tbfMouseInArrow] * FToolButtonFlags <> []) then begin Exclude(FToolButtonFlags, tbfPressed); Exclude(FToolButtonFlags, tbfArrowPressed); Exclude(FToolButtonFlags, tbfMouseInArrow); end; SetMouseInControl(false); end; end; procedure TToolButton.MouseMove(Shift: TShiftState; X, Y: Integer); var NewFlags: TToolButtonFlags; begin inherited MouseMove(Shift, X, Y); if (not MouseCapture) and (Style = tbsDropDown) and (FToolBar <> nil) then begin NewFlags := FToolButtonFlags; if PointInArrow(X, Y) then Include(NewFlags, tbfMouseInArrow) else Exclude(NewFlags, tbfMouseInArrow); if NewFlags <> FToolButtonFlags then begin FToolButtonFlags := NewFlags; Invalidate; end; end; end; procedure TToolButton.SetDown(Value: Boolean); var StartIndex, EndIndex: integer; i: Integer; CurButton: TToolButton; begin if Value = FDown then exit; if csLoading in ComponentState then begin FDown := Value; Exit; end; //DebugLn('TToolButton.SetDown ',Style=tbsCheck,',',FDown,',',GroupAllUpAllowed); if Value or (Style <> tbsCheck) or GroupAllUpAllowed then begin FDown := Value; Invalidate; end; // uncheck all other in the group if GetGroupBounds(StartIndex, EndIndex) then // this also checks Toolbar, Grouped and Style for i := StartIndex to EndIndex do begin CurButton := FToolBar.Buttons[i]; if CurButton.FDown and (CurButton <> Self) then begin CurButton.FDown := False; CurButton.Invalidate; end; end; end; procedure TToolButton.SetDropdownMenu(Value: TPopupMenu); begin if Value = FDropdownMenu then exit; FDropdownMenu := Value; if Assigned(Value) then Value.FreeNotification(Self); end; procedure TToolButton.SetGrouped(Value: Boolean); var StartIndex, EndIndex: integer; i, j: Integer; begin if FGrouped = Value then exit; FGrouped := Value; if csLoading in ComponentState then exit; // make sure, that only one button in a group is checked if GetGroupBounds(StartIndex, EndIndex) then // this also checks Toolbar, Grouped and Style for i := StartIndex to EndIndex - 1 do // no need check last button if FToolBar.Buttons[i].FDown then // uncheck other buttons for j := i + 1 to EndIndex do if FToolBar.Buttons[j].FDown then begin FToolBar.Buttons[j].FDown := false; FToolBar.Buttons[j].Invalidate; end; end; procedure TToolButton.SetImageIndex(Value: TImageIndex); begin if FImageIndex = Value then exit; FImageIndex := Value; if IsControlVisible and Assigned(FToolBar) then Invalidate; end; procedure TToolButton.SetMarked(Value: Boolean); begin if FMarked = Value then exit; FMarked := Value; if FToolBar <> nil then Invalidate; end; procedure TToolButton.SetIndeterminate(Value: Boolean); begin if FIndeterminate = Value then exit; if Value then SetDown(False); FIndeterminate := Value; if FToolBar <> nil then Invalidate; end; procedure TToolButton.SetMenuItem(Value: TMenuItem); begin if Value = FMenuItem then Exit; // copy values from menuitem // is menuitem is still loading, skip this if Assigned(Value) and not (csLoading in Value.ComponentState) then CopyPropertiesFromMenuItem(Value); FMenuItem := Value; if FMenuItem <> nil then FMenuItem.FreeNotification(Self); end; procedure TToolButton.SetShowCaption(const AValue: boolean); begin if FShowCaption=AValue then exit; FShowCaption:=AValue; if IsControlVisible then begin InvalidatePreferredSize; UpdateVisibleToolbar; end; end; procedure TToolButton.SetStyle(Value: TToolButtonStyle); begin if FStyle = Value then exit; FStyle := Value; case Value of tbsSeparator: begin Width := cDefSeparatorWidth; Height := cDefSeparatorWidth; end; tbsDivider: begin Width := cDefDividerWidth; Height := cDefDividerWidth; end; end; InvalidatePreferredSize; if IsControlVisible then UpdateVisibleToolbar; end; procedure TToolButton.SetWrap(Value: Boolean); begin if FWrap = Value then exit; FWrap := Value; if Assigned(FToolBar) then RefreshControl; end; procedure TToolButton.TextChanged; begin inherited TextChanged; if FToolbar = nil then Exit; if FToolbar.ShowCaptions then Invalidate; end; procedure TToolButton.SetMouseInControl(NewMouseInControl: Boolean); begin //DebugLn('TToolButton.SetMouseInControl A ',Name,' Old=',FMouseInControl,' New=',NewMouseInControl); if FMouseInControl = NewMouseInControl then exit; FMouseInControl := NewMouseInControl; //DebugLn('TToolButton.SetMouseInControl B ',Name,' Now=',FMouseInControl,' Down=',Down); Invalidate; end; procedure TToolButton.CMEnabledChanged(var Message: TLMEssage); begin inherited; invalidate; end; procedure TToolButton.CMVisibleChanged(var Message: TLMessage); begin if FToolBar <> nil then RefreshControl; end; procedure TToolButton.BeginUpdate; begin Inc(FUpdateCount); end; procedure TToolButton.EndUpdate; begin Dec(FUpdateCount); end; {------------------------------------------------------------------------------- function TToolButton.GetGroupBounds(out StartIndex, EndIndex: integer): boolean; Return the index of the first and the last ToolButton in the group. returns true only if: ToolBar assigned Style is tbsCheck Grouped is true all buttons in range is assigned one or more buttons in a group else returns false (and StartIndex = EndIndex = -1) -------------------------------------------------------------------------------} function TToolButton.GetGroupBounds(out StartIndex, EndIndex: integer): boolean; var CurButton: TToolButton; begin result := Grouped and (Style = tbsCheck) and Assigned(FToolBar); if not result then begin StartIndex := -1; EndIndex := -1; exit; end; StartIndex := Index; EndIndex := StartIndex; while StartIndex > 0 do begin CurButton := FToolBar.Buttons[StartIndex - 1]; if not Assigned(CurButton) then break; if not CurButton.Grouped then break; if not (CurButton.Style in [tbsCheck, tbsSeparator, tbsDivider]) then break; dec(StartIndex); end; while EndIndex < (FToolBar.FButtons.Count - 1) do begin CurButton := FToolBar.Buttons[EndIndex + 1]; if not Assigned(CurButton) then break; if not CurButton.Grouped then break; if not (CurButton.Style in [tbsCheck, tbsSeparator, tbsDivider]) then break; inc(EndIndex); end; end; function TToolButton.GetIndex: Integer; begin if Assigned(FToolBar) then Result := FToolBar.FButtons.IndexOf(Self) else Result := -1; end; function TToolButton.GetTextSize: TSize; var S: String; begin S := Caption; DeleteAmpersands(S); Result := Canvas.TextExtent(S) end; procedure TToolButton.GetPreferredSize( var PreferredWidth, PreferredHeight: integer; Raw: boolean; WithThemeSpace: boolean); var RealButtonWidth, RealButtonHeight: Integer; begin inherited GetPreferredSize(PreferredWidth, PreferredHeight, Raw, WithThemeSpace); if FToolbar = nil then Exit; RealButtonWidth := FToolbar.ButtonWidth; RealButtonHeight := FToolbar.ButtonHeight; if RealButtonHeight <= 0 then Exit; // buttonheight overrules in hor toolbar if FToolBar.IsVertical then PreferredWidth := RealButtonWidth else PreferredHeight := RealButtonHeight; end; function TToolButton.IsWidthStored: Boolean; begin Result := Style in [tbsSeparator, tbsDivider]; if FToolBar<>nil then Result := Result and FToolBar.IsVertical; end; procedure TToolButton.RefreshControl; begin UpdateControl; end; procedure TToolButton.UpdateControl; begin UpdateVisibleToolbar; end; function TToolButton.CheckMenuDropdown: Boolean; begin Result := (not (csDesigning in ComponentState)) and ((Assigned(DropdownMenu) and (DropdownMenu.AutoPopup)) or Assigned(MenuItem)) and Assigned(FToolBar); if Result then begin Include(FToolButtonFlags, tbfDropDownMenuShown); try Result := FToolBar.CheckMenuDropdown(Self); finally Exclude(FToolButtonFlags, tbfDropDownMenuShown); end; end; end; procedure TToolButton.Click; begin inherited Click; end; procedure TToolButton.GetCurrentIcon(var ImageList: TCustomImageList; var TheIndex: integer; var TheEffect: TGraphicsDrawEffect); var UseAutoEffects: Integer; begin ImageList := nil; TheIndex := -1; TheEffect := gdeNormal; UseAutoEffects := ThemeServices.GetOption(toUseGlyphEffects); if (ImageIndex < 0) or (FToolBar = nil) then Exit; if Style in [tbsButton, tbsDropDown, tbsButtonDrop, tbsCheck] then begin TheIndex := ImageIndex; ImageList := FToolBar.Images; if (FToolButtonFlags*[tbfPressed,tbfArrowPressed] = [tbfPressed]) then begin // if button pressed then use PressedImages // Maybe To-Do ? {if (FToolBar.PressedImages <> nil) and (ImageIndex < FToolBar.PressedImages.Count) then ImageList := FToolBar.DisabledImages else} if UseAutoEffects > 0 then TheEffect := gdeShadowed; end else if Enabled and FMouseInControl then begin // if mouse over button then use HotImages if (FToolBar.HotImages <> nil) and (ImageIndex < FToolBar.HotImages.Count) then ImageList := FToolBar.HotImages else if UseAutoEffects > 0 then TheEffect := gdeHighlighted; end else if not Enabled then begin // if button disabled then use DisabledImages if (FToolBar.DisabledImages <> nil) and (ImageIndex < FToolBar.DisabledImages.Count) then ImageList := FToolBar.DisabledImages else TheEffect := gdeDisabled; end; end; end; function TToolButton.IsCheckedStored: Boolean; begin Result := (ActionLink = nil) or not TToolButtonActionLink(ActionLink).IsCheckedLinked; end; function TToolButton.IsHeightStored: Boolean; begin Result := Style in [tbsSeparator, tbsDivider]; if FToolBar<>nil then Result := Result and not FToolBar.IsVertical; end; function TToolButton.IsImageIndexStored: Boolean; begin Result := (ActionLink = nil) or not TToolButtonActionLink(ActionLink).IsImageIndexLinked; end; procedure TToolButton.AssignTo(Dest: TPersistent); begin inherited AssignTo(Dest); if Dest is TCustomAction then begin TCustomAction(Dest).Checked := Down; TCustomAction(Dest).ImageIndex := ImageIndex; end; end; function TToolButton.GetButtonDrawDetail: TThemedElementDetails; var ToolDetail: TThemedToolBar; begin if Style = tbsDropDown then ToolDetail := ttbSplitButtonNormal else if Style in [tbsDivider, tbsSeparator] then if FToolBar.IsVertical then ToolDetail := ttbSeparatorVertNormal else ToolDetail := ttbSeparatorNormal else ToolDetail := ttbButtonNormal; if not Enabled then inc(ToolDetail, 3) // ttbButtonDisabled else begin if Down then begin // checked states if (tbfPressed in FToolButtonFlags) and FMouseInControl then inc(ToolDetail, 2) // ttbButtonPressed else if FMouseInControl then inc(ToolDetail, 5) // ttbButtonCheckedHot else inc(ToolDetail, 4);// ttbButtonChecked end else begin if (tbfPressed in FToolButtonFlags) and FMouseInControl then inc(ToolDetail, 2) // ttbButtonPressed else if FMouseInControl then inc(ToolDetail, 1);// ttbButtonHot end; end; Result := ThemeServices.GetElementDetails(ToolDetail); end; procedure TToolButton.SetParent(AParent: TWinControl); var i: Integer; NewWidth: Integer; NewHeight: Integer; begin CheckNewParent(AParent); if AParent=Parent then exit; // remove from old button list if Assigned(FToolBar) then FToolBar.RemoveButton(Self); FToolBar := nil; if AParent is TToolBar then begin if not TToolBar(AParent).IsVertical then begin if Style in [tbsButton,tbsDropDown,tbsButtonDrop,tbsCheck] then NewWidth := TToolBar(AParent).ButtonWidth else NewWidth := Width; NewHeight := TToolBar(AParent).ButtonHeight; end else begin if Style in [tbsButton,tbsDropDown,tbsButtonDrop,tbsCheck] then NewHeight := TToolBar(AParent).ButtonHeight else NewHeight := Height; NewWidth := TToolBar(AParent).ButtonWidth; end; SetBoundsKeepBase(Left, Top, NewWidth, NewHeight); end; // inherited inherited SetParent(AParent); // add to new button list if Parent is TToolBar then begin FToolBar := TToolBar(Parent); i := Index; if i < 0 then FToolBar.AddButton(Self); UpdateVisibleToolbar; end; //DebugLn(['TToolButton.SetParent A ',Name,' NewIndex=',Index]); end; procedure TToolButton.UpdateVisibleToolbar; begin //DebugLn('TToolButton.UpdateVisibleToolbar ',Parent is TToolBar); if Parent is TToolBar then TToolBar(Parent).UpdateVisibleBar; end; function TToolButton.GroupAllUpAllowed: boolean; var StartIndex, EndIndex: integer; i: Integer; begin if not GetGroupBounds(StartIndex, EndIndex) then // this also checks Toolbar, Grouped and Style exit(true); // allow all up, if one button has AllowAllUp for i := StartIndex to EndIndex do if FToolBar.Buttons[i].AllowAllUp then exit(true); exit(false); end; function TToolButton.DialogChar(var Message: TLMKey): boolean; begin if IsAccel(Message.CharCode, Caption) and FToolBar.ShowCaptions then begin Click; Result := true; end else Result := inherited; end; procedure TToolButton.CalculatePreferredSize(var PreferredWidth, PreferredHeight: integer; WithThemeSpace: Boolean); var IconSize: TSize; TextSize: TSize; TextPos: TPoint; IconPos: TPoint; dist: Integer; ImgList: TCustomImageList; ImgIndex: integer; ImgEffect: TGraphicsDrawEffect; begin if Assigned(FToolBar) then begin PreferredWidth := 0; PreferredHeight := 0; // calculate text size TextSize.cx := 0; TextSize.cy := 0; if (Style in [tbsButton, tbsDropDown, tbsButtonDrop, tbsCheck]) and (FToolBar.ShowCaptions) and //Allow hide caption only in list mode ((FToolBar.List and ShowCaption) or not FToolBar.List) then begin if (Caption<>'') then begin if FToolBar.HandleAllocated then TextSize := GetTextSize; end; // add space around text dist := FToolbar.Scale96ToFont(4); inc(TextSize.cx, dist); inc(TextSize.cy, dist); end; // calculate icon size IconSize := Size(0, 0); if (Style in [tbsButton, tbsDropDown, tbsButtonDrop, tbsCheck]) then begin GetCurrentIcon(ImgList, ImgIndex, ImgEffect); if Assigned(ImgList) then begin IconSize := ImgList.SizeForPPI[FToolBar.ImagesWidth, FToolBar.Font.PixelsPerInch]; if IconSize.cy <= 0 then IconSize.cx := 0; end; end; // calculate text and icon position TextPos := Point(0, 0); IconPos := Point(0, 0); if TextSize.cx > 0 then begin if IconSize.cx > 0 then begin if FToolBar.List then begin // icon left of text dist := FToolbar.Scale96ToFont(cHorIconTextDist); TextPos.X := IconPos.X + IconSize.cx + dist; end else begin // icon above text dist := FToolbar.Scale96ToFont(cVertIconTextDist); TextPos.Y := IconPos.Y + IconSize.cy + dist; end; end else begin // only text end; end else if IconSize.cx > 0 then begin // only icon end; PreferredWidth := Max(IconPos.X + IconSize.cx, TextPos.X + TextSize.cx); PreferredHeight := Max(IconPos.Y + IconSize.cy, TextPos.Y + TextSize.cy); //DebugLn(['TToolButton.CalculatePreferredSize Preferred=',PreferredWidth,',',PreferredHeight,' Icon=',IconPos.X,'+',IconSize.Width,' Text=',TextPos.X,'+',TextSize.cx]); //DebugLn(['TToolButton.CalculatePreferredSize Preferred=',PreferredWidth,',',PreferredHeight,' Icon=',IconPos.Y,'+',IconSize.Height,' Text=',TextPos.Y,'+',TextSize.cy]); // add button frame if (Style in [tbsButton, tbsDropDown, tbsButtonDrop, tbsCheck]) then begin inc(PreferredWidth, 4); inc(PreferredHeight, 4); PreferredWidth := Max(PreferredWidth, FToolBar.ButtonWidth); PreferredHeight := Max(PreferredHeight, FToolBar.ButtonHeight); case Style of tbsDropDown: inc(PreferredWidth, FToolBar.DropDownWidth); tbsButtonDrop: inc(PreferredWidth, FToolBar.ButtonDropWidth-cDefButtonDropDecArrowWidth); end; end else if Style = tbsDivider then if FToolBar.IsVertical then PreferredHeight := cDefDividerWidth else PreferredWidth := cDefDividerWidth else if Style = tbsSeparator then if FToolBar.IsVertical then PreferredHeight := cDefSeparatorWidth else PreferredWidth := cDefSeparatorWidth; end; //DebugLn(['TToolButton.CalculatePreferredSize ',DbgSName(Self),' ',PreferredWidth,',',PreferredHeight,' Caption=',Caption]); end; class function TToolButton.GetControlClassDefaultSize: TSize; begin Result.CX := 23; Result.CY := 22; end; // included by comctrls.pp