mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-05-29 13:22:37 +02:00
413 lines
13 KiB
ObjectPascal
413 lines
13 KiB
ObjectPascal
unit QtThemes;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
{$I qtdefines.inc}
|
|
|
|
uses
|
|
// rtl
|
|
Types, Classes, SysUtils,
|
|
// qt bindings
|
|
qt4,
|
|
// lcl
|
|
LCLType, LCLProc, LCLIntf, Graphics, Themes, TmSchema,
|
|
// widgetset
|
|
InterfaceBase, QtObjects
|
|
;
|
|
|
|
type
|
|
TQtDrawVariant =
|
|
(
|
|
qdvNone,
|
|
qdvPrimitive,
|
|
qdvControl,
|
|
qdvComplexControl,
|
|
qdvStandardPixmap
|
|
);
|
|
TQtDrawElement = record
|
|
case DrawVariant: TQtDrawVariant of
|
|
qdvPrimitive:
|
|
(PrimitiveElement: QStylePrimitiveElement);
|
|
qdvControl:
|
|
(ControlElement: QStyleControlElement);
|
|
qdvComplexControl:
|
|
(ComplexControl: QStyleComplexControl;
|
|
SubControls: QStyleSubControls);
|
|
qdvStandardPixmap:
|
|
(StandardPixmap: QStyleStandardPixmap);
|
|
end;
|
|
|
|
{ TQtThemeServices }
|
|
|
|
TQtThemeServices = class(TThemeServices)
|
|
private
|
|
FStyle: QStyleH;
|
|
function GetStyle: QStyleH;
|
|
protected
|
|
function InitThemes: Boolean; override;
|
|
function UseThemes: Boolean; override;
|
|
function ThemedControlsEnabled: Boolean; override;
|
|
procedure InternalDrawParentBackground(Window: HWND; Target: HDC; Bounds: PRect); override;
|
|
|
|
function GetControlState(Details: TThemedElementDetails): QStyleState;
|
|
function GetDetailSize(Details: TThemedElementDetails): Integer; override;
|
|
function GetDrawElement(Details: TThemedElementDetails): TQtDrawElement;
|
|
property Style: QStyleH read GetStyle;
|
|
public
|
|
procedure DrawElement(DC: HDC; Details: TThemedElementDetails; const R: TRect; ClipRect: PRect); override;
|
|
procedure DrawEdge(DC: HDC; Details: TThemedElementDetails; const R: TRect; Edge, Flags: Cardinal; AContentRect: PRect); override;
|
|
procedure DrawIcon(DC: HDC; Details: TThemedElementDetails; const R: TRect; himl: HIMAGELIST; Index: Integer); override;
|
|
|
|
function ContentRect(DC: HDC; Details: TThemedElementDetails; BoundingRect: TRect): TRect; override;
|
|
function HasTransparentParts(Details: TThemedElementDetails): Boolean; override;
|
|
end;
|
|
|
|
implementation
|
|
|
|
{ TQtThemeServices }
|
|
|
|
function TQtThemeServices.GetStyle: QStyleH;
|
|
begin
|
|
if FStyle = nil then
|
|
FStyle := QApplication_style();
|
|
Result := FStyle;
|
|
end;
|
|
|
|
function TQtThemeServices.InitThemes: Boolean;
|
|
begin
|
|
FStyle := nil;
|
|
Result := True;
|
|
end;
|
|
|
|
function TQtThemeServices.UseThemes: Boolean;
|
|
begin
|
|
Result := True;
|
|
end;
|
|
|
|
function TQtThemeServices.ThemedControlsEnabled: Boolean;
|
|
begin
|
|
Result := True;
|
|
end;
|
|
|
|
function TQtThemeServices.ContentRect(DC: HDC;
|
|
Details: TThemedElementDetails; BoundingRect: TRect): TRect;
|
|
begin
|
|
Result := BoundingRect;
|
|
InflateRect(Result, -1, -1);
|
|
end;
|
|
|
|
procedure TQtThemeServices.DrawEdge(DC: HDC;
|
|
Details: TThemedElementDetails; const R: TRect; Edge, Flags: Cardinal;
|
|
AContentRect: PRect);
|
|
begin
|
|
|
|
end;
|
|
|
|
procedure TQtThemeServices.DrawElement(DC: HDC;
|
|
Details: TThemedElementDetails; const R: TRect; ClipRect: PRect);
|
|
var
|
|
Context: TQtDeviceContext absolute DC;
|
|
opt: QStyleOptionH;
|
|
ARect: TRect;
|
|
AIcon: QIconH;
|
|
Element: TQtDrawElement;
|
|
Features: QStyleOptionButtonButtonFeatures;
|
|
Position: QStyleOptionHeaderSectionPosition;
|
|
begin
|
|
if (Context <> nil) then
|
|
begin
|
|
ARect := R;
|
|
Element := GetDrawElement(Details);
|
|
case Element.DrawVariant of
|
|
qdvControl:
|
|
begin
|
|
if (Element.ControlElement in [QStyleCE_PushButton, QStyleCE_RadioButton, QStyleCE_CheckBox]) then
|
|
begin
|
|
opt := QStyleOptionButton_create();
|
|
Features := QStyleOptionButtonNone;
|
|
if Details.Element = teToolBar then
|
|
Features := Features or QStyleOptionButtonFlat;
|
|
QStyleOptionButton_setFeatures(QStyleOptionButtonH(opt), Features);
|
|
end
|
|
else
|
|
if (Element.ControlElement = QStyleCE_HeaderSection) then
|
|
begin
|
|
opt := QStyleOptionHeader_create();
|
|
case Details.Part of
|
|
HP_HEADERITEM: Position := QStyleOptionHeaderMiddle;
|
|
HP_HEADERITEMLEFT: Position := QStyleOptionHeaderBeginning;
|
|
HP_HEADERITEMRIGHT: Position := QStyleOptionHeaderEnd;
|
|
end;
|
|
QStyleOptionHeader_setPosition(QStyleOptionHeaderH(opt), Position);
|
|
QStyleOptionHeader_setOrientation(QStyleOptionHeaderH(opt), QtHorizontal);
|
|
end
|
|
else
|
|
opt := QStyleOptionComplex_create(LongInt(QStyleOptionVersion), LongInt(QStyleOptionSO_Default));
|
|
|
|
QStyleOption_setState(opt, GetControlState(Details));
|
|
QStyleOption_setRect(opt, @ARect);
|
|
|
|
QStyle_drawControl(Style, Element.ControlElement, opt, Context.Widget);
|
|
QStyleOption_Destroy(opt);
|
|
end;
|
|
qdvComplexControl:
|
|
begin
|
|
Context.save;
|
|
case Element.ComplexControl of
|
|
QStyleCC_ToolButton: opt := QStyleOptionToolButton_create();
|
|
|
|
QStyleCC_TitleBar, QStyleCC_MdiControls:
|
|
begin
|
|
opt := QStyleOptionTitleBar_create();
|
|
QStyleOptionTitleBar_setTitleBarFlags(QStyleOptionTitleBarH(opt), QtWindow or QtWindowSystemMenuHint);
|
|
// workaround: qt has own minds about position of requested part -
|
|
// but we need a way to draw it at our position
|
|
Context.translate(ARect.Left, ARect.Top);
|
|
OffsetRect(ARect, -ARect.Left, -ARect.Top);
|
|
end;
|
|
else
|
|
opt := QStyleOptionComplex_create(LongInt(QStyleOptionVersion), LongInt(QStyleOptionSO_Default));
|
|
end;
|
|
|
|
QStyleOptionComplex_setSubControls(QStyleOptionComplexH(opt), Element.SubControls);
|
|
|
|
QStyleOption_setState(opt, GetControlState(Details));
|
|
QStyleOption_setRect(opt, @ARect);
|
|
QStyle_drawComplexControl(Style, Element.ComplexControl, QStyleOptionComplexH(opt), Context.Widget);
|
|
QStyleOption_Destroy(opt);
|
|
Context.restore;
|
|
end;
|
|
qdvPrimitive:
|
|
begin
|
|
opt := QStyleOption_create(Integer(QStyleOptionVersion), Integer(QStyleOptionSO_Default));
|
|
QStyleOption_setState(opt, GetControlState(Details));
|
|
QStyleOption_setRect(opt, @ARect);
|
|
QStyle_drawPrimitive(Style, Element.PrimitiveElement, opt, Context.Widget);
|
|
QStyleOption_Destroy(opt);
|
|
end;
|
|
qdvStandardPixmap:
|
|
begin
|
|
opt := QStyleOption_create(Integer(QStyleOptionVersion), Integer(QStyleOptionSO_Default));
|
|
AIcon := QIcon_create();
|
|
QStyle_standardIcon(Style, AIcon, Element.StandardPixmap, opt);
|
|
QIcon_paint(AIcon, Context.Widget, ARect.Left, ARect.Top,
|
|
ARect.Right - ARect.Left, ARect.Bottom - ARect.Top);
|
|
QIcon_destroy(AIcon);
|
|
QStyleOption_Destroy(opt);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TQtThemeServices.DrawIcon(DC: HDC;
|
|
Details: TThemedElementDetails; const R: TRect; himl: HIMAGELIST;
|
|
Index: Integer);
|
|
begin
|
|
|
|
end;
|
|
|
|
function TQtThemeServices.HasTransparentParts(Details: TThemedElementDetails): Boolean;
|
|
begin
|
|
Result := True;
|
|
end;
|
|
|
|
procedure TQtThemeServices.InternalDrawParentBackground(Window: HWND;
|
|
Target: HDC; Bounds: PRect);
|
|
begin
|
|
// ?
|
|
end;
|
|
|
|
function TQtThemeServices.GetControlState(Details: TThemedElementDetails): QStyleState;
|
|
begin
|
|
{
|
|
QStyleState_None
|
|
QStyleState_Enabled
|
|
QStyleState_Raised
|
|
QStyleState_Sunken
|
|
QStyleState_Off
|
|
QStyleState_NoChange
|
|
QStyleState_On
|
|
QStyleState_DownArrow
|
|
QStyleState_Horizontal
|
|
QStyleState_HasFocus
|
|
QStyleState_Top
|
|
QStyleState_Bottom
|
|
QStyleState_FocusAtBorder
|
|
QStyleState_AutoRaise
|
|
QStyleState_MouseOver
|
|
QStyleState_UpArrow
|
|
QStyleState_Selected
|
|
QStyleState_Active
|
|
QStyleState_Open
|
|
QStyleState_Children
|
|
QStyleState_Item
|
|
QStyleState_Sibling
|
|
QStyleState_Editing
|
|
QStyleState_KeyboardFocusChange
|
|
QStyleState_ReadOnly
|
|
}
|
|
Result := QStyleState_None;
|
|
|
|
if not IsDisabled(Details) then
|
|
Result := Result or QStyleState_Enabled;
|
|
|
|
if IsHot(Details) then
|
|
Result := Result or QStyleState_MouseOver;
|
|
|
|
if IsPushed(Details) then
|
|
Result := Result or QStyleState_Sunken;
|
|
|
|
if IsChecked(Details) then
|
|
Result := Result or QStyleState_On
|
|
else
|
|
if IsMixed(Details) then
|
|
Result := Result or QStyleState_NoChange;
|
|
|
|
// specific states
|
|
|
|
// define orientations
|
|
if ((Details.Element = teRebar) and (Details.Part = RP_GRIPPER)) or
|
|
((Details.Element = teToolBar) and (Details.Part = TP_SEPARATOR)) then
|
|
Result := Result or QStyleState_Horizontal;
|
|
end;
|
|
|
|
function TQtThemeServices.GetDetailSize(Details: TThemedElementDetails): Integer;
|
|
begin
|
|
case Details.Element of
|
|
teRebar :
|
|
if Details.Part in [RP_GRIPPER, RP_GRIPPERVERT] then
|
|
Result := -1;
|
|
else
|
|
Result := inherited;
|
|
end;
|
|
end;
|
|
|
|
function TQtThemeServices.GetDrawElement(Details: TThemedElementDetails): TQtDrawElement;
|
|
const
|
|
ButtonMap: array[BP_PUSHBUTTON..BP_USERBUTTON] of QStyleControlElement =
|
|
(
|
|
{BP_PUSHBUTTON } QStyleCE_PushButton,
|
|
{BP_RADIOBUTTON} QStyleCE_RadioButton,
|
|
{BP_CHECKBOX } QStyleCE_CheckBox,
|
|
{BP_GROUPBOX } QStyleCE_PushButton,
|
|
{BP_USERBUTTON } QStyleCE_PushButton
|
|
);
|
|
begin
|
|
Result.DrawVariant := qdvNone;
|
|
case Details.Element of
|
|
teButton:
|
|
begin
|
|
if Details.Part <> BP_GROUPBOX then
|
|
begin
|
|
Result.DrawVariant := qdvControl;
|
|
Result.ControlElement := ButtonMap[Details.Part]
|
|
end
|
|
else
|
|
begin
|
|
Result.DrawVariant := qdvComplexControl;
|
|
Result.ComplexControl := QStyleCC_GroupBox;
|
|
Result.SubControls := QStyleSC_GroupBoxFrame;
|
|
end;
|
|
end;
|
|
teHeader:
|
|
begin
|
|
case Details.Part of
|
|
HP_HEADERITEM,
|
|
HP_HEADERITEMLEFT,
|
|
HP_HEADERITEMRIGHT:
|
|
begin
|
|
Result.DrawVariant := qdvControl;
|
|
Result.ControlElement := QStyleCE_HeaderSection;
|
|
end;
|
|
HP_HEADERSORTARROW:
|
|
begin
|
|
Result.DrawVariant := qdvPrimitive;
|
|
Result.PrimitiveElement := QStylePE_IndicatorHeaderArrow;
|
|
end;
|
|
end;
|
|
end;
|
|
teToolBar:
|
|
begin
|
|
case Details.Part of
|
|
TP_BUTTON,
|
|
TP_DROPDOWNBUTTON,
|
|
TP_SPLITBUTTON: // there is another positibility to draw TP_SPLITBUTTON by CC_ToolButton
|
|
begin
|
|
Result.DrawVariant := qdvPrimitive;
|
|
Result.PrimitiveElement := QStylePE_PanelButtonTool;
|
|
end;
|
|
TP_SPLITBUTTONDROPDOWN:
|
|
begin
|
|
Result.DrawVariant := qdvPrimitive;
|
|
Result.PrimitiveElement := QStylePE_IndicatorButtonDropDown;
|
|
end;
|
|
TP_SEPARATOR,
|
|
TP_SEPARATORVERT:
|
|
begin
|
|
Result.DrawVariant := qdvPrimitive;
|
|
Result.PrimitiveElement := QStylePE_IndicatorToolBarSeparator;
|
|
end;
|
|
end;
|
|
end;
|
|
teRebar:
|
|
begin
|
|
case Details.Part of
|
|
RP_GRIPPER, RP_GRIPPERVERT: // used in splitter
|
|
begin
|
|
Result.DrawVariant := qdvControl;
|
|
Result.ControlElement := QStyleCE_Splitter;
|
|
end;
|
|
end;
|
|
end;
|
|
teWindow:
|
|
begin
|
|
case Details.Part of
|
|
WP_SYSBUTTON: Result.SubControls := QStyleSC_TitleBarSysMenu;
|
|
WP_MINBUTTON: Result.SubControls := QStyleSC_TitleBarMinButton;
|
|
WP_MAXBUTTON: Result.SubControls := QStyleSC_TitleBarMaxButton;
|
|
WP_CLOSEBUTTON: Result.SubControls := QStyleSC_TitleBarCloseButton;
|
|
WP_SMALLCLOSEBUTTON: Result.SubControls := QStyleSC_TitleBarCloseButton;
|
|
WP_RESTOREBUTTON: Result.SubControls := QStyleSC_TitleBarNormalButton;
|
|
WP_HELPBUTTON: Result.SubControls := QStyleSC_TitleBarContextHelpButton;
|
|
WP_MDIHELPBUTTON: Result.SubControls := QStyleSC_TitleBarContextHelpButton;
|
|
WP_MDIMINBUTTON: Result.SubControls := QStyleSC_MdiMinButton;
|
|
WP_MDICLOSEBUTTON: Result.SubControls := QStyleSC_MdiCloseButton;
|
|
WP_MDIRESTOREBUTTON: Result.SubControls := QStyleSC_MdiNormalButton;
|
|
else
|
|
Result.SubControls := QStyleSC_None;
|
|
end;
|
|
|
|
if Result.SubControls >= QStyleSC_MdiMinButton then
|
|
Result.ComplexControl := QStyleCC_MdiControls
|
|
else
|
|
Result.ComplexControl := QStyleCC_TitleBar;
|
|
|
|
Result.DrawVariant := qdvComplexControl;
|
|
{
|
|
// maybe through icon
|
|
Result.DrawVariant := qdvStandardPixmap;
|
|
case Details.Part of
|
|
WP_MINBUTTON: Result.StandardPixmap := QStyleSP_TitleBarMinButton;
|
|
WP_MDIMINBUTTON: Result.StandardPixmap := QStyleSP_TitleBarMinButton;
|
|
WP_MAXBUTTON: Result.StandardPixmap := QStyleSP_TitleBarMaxButton;
|
|
WP_CLOSEBUTTON: Result.StandardPixmap := QStyleSP_TitleBarCloseButton;
|
|
WP_SMALLCLOSEBUTTON: Result.StandardPixmap := QStyleSP_TitleBarCloseButton;
|
|
WP_MDICLOSEBUTTON: Result.StandardPixmap := QStyleSP_TitleBarCloseButton;
|
|
WP_RESTOREBUTTON: Result.StandardPixmap := QStyleSP_TitleBarNormalButton;
|
|
WP_MDIRESTOREBUTTON: Result.StandardPixmap := QStyleSP_TitleBarNormalButton;
|
|
WP_HELPBUTTON: Result.StandardPixmap := QStyleSP_TitleBarContextHelpButton;
|
|
WP_MDIHELPBUTTON: Result.StandardPixmap := QStyleSP_TitleBarContextHelpButton;
|
|
else
|
|
Result.StandardPixmap := QStyleSP_TitleBarCloseButton;
|
|
end;
|
|
}
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
end.
|
|
|
|
|