If you're using Lazarus with Qt - you can actually leverage Qt qss stylesheets to get an easy way to style your user interface. In fact, it has more features than lcl provides by default. QSS is a css-like language that lets you define the look of your UI, but also lets you set up animations, mouse-over effects, and so on. You could include a qss file alongside your application, but you can also bake the qss file into your executable using resources. Here's how: ================================================= Step 1: Include the qss stylesheet as a resource: ================================================= * First, create or copy the qss file in your source folder. * Project->Project options, Resources, click (+) Add. Select the qss file, should show up as a relative path. Set the resource type to RCDATA, give it a name. =================================== Step 2: Load and use the qss style: =================================== You can do this when you load your application, or before you the affected controls/forms/windows - Your main window constructor is a good place, for example - in the example below, I'm applying the stylesheet to the main window, which means that you can define styles for the main window and any of it's children in the qss stylesheet, which is typically what you'd want - but you can in theory apply the style to any control, provided you map the LCL control to it's equivalent Qt control type. ===================================================================== A note on control names. At this time, LCL's Qt widget implementation does not pass the widget name to the Qt widget. Consequentially, qss selectors by control name will not work out of the box, for instance: QLabel#LblTitle { color: #0f0; } ...will not work by default, because qt has no idea what control is named 'LblTitle', even if you set the name in lazarus. The workaround for this is to explicitly set the name in your code, for instance: ... const TitleName: UnicodeString = 'LblTitle'; begin QObject_setObjectName( QObjectH(TQtWidget(LblTitle.Handle).Widget), @TitleName ); end; ... Creative minds can probably come up with a way to automate this for all controls - but it would be even nicer if the LCL actually were to implement this at some point in the future. In fact, all of this is a bit of a hack, and a formalized stylesheet interface would be nice to have (tm). (actually, i did end up writing this code, see way below). ===================================================================== Example loading code: ... uses classes, qt6, qtobjects, qtwidgets; ... constructor TMainWindowForm.Create(AOwner: TComponent); var NativeControl: TQtMainWindow; ResourceStream: TResourceStream; ResourceText: TStringList; const TitleName: UnicodeString = 'LblTitle'; begin inherited Create(AOwner); Randomize; NativeControl := TQtMainWindow(Self.Handle); ResourceStream := TResourceStream.Create(HInstance, 'QTSTYLE', RT_RCDATA); try ResourceText := TStringList.Create; try ResourceText.LoadFromStream(ResourceStream); NativeControl.StyleSheet := unicodestring(ResourceText.Text); finally FreeAndNil(ResourceText); end; finally FreeAndNil(ResourceStream); end; (* Optional, set the Qt name of specific controls if needed *) QObject_setObjectName( QObjectH(TQtWidget(LblTitle.Handle).Widget), @TitleName ); end; ===========================+========================================= LCL -> QT control mapping: | ---------------------------' This isn't a complete list - you can figure this out by examining the lazarus source code if you need to, but I figure I'd help ya along :) +--------------+-----------------+----------------------------------+ | LCL Control | Qt equivalent | Description | +--------------+-----------------+----------------------------------+ | TEdit | QLineEdit | Single-line text input. | | TMemo | QTextEdit | Multi-line text editor. | | TLabel | -- n/a -- | Qt is not used to render these. | | TStaticText | QLabel | TLabel but with a win. handle. | | TButton | QPushButton | Standard command button. | | TCheckBox | QCheckBox | Binary toggle with text. | | TRadioButton | QRadioButton | Exclusive selection toggle. | | TComboBox | QComboBox | Dropdown selection list. | | TListBox | QListWidget | Scrollable list of items. | | TForm | QMainWindow | Window (also TMainWindow) | | TPanel | QFrame | Generic container. | | TGroupBox | QGroupBox | Titled container for grouping. | | TScrollBox | QScrollArea | Container with scrollbars. | | TPageControl | QTabWidget | Tabbed container control. | | TTabSheet | QWidget | Individual page/tab content area.| | TSplitter | QSplitterHandle | Draggable bar between panels. | +--------------+-----------------+----------------------------------+ +----------------+-------------------------+ | LCL Control | Qt equivalent | +----------------+-------------------------+ | TProgressBar | QProgressBar | | TTrackBar | QSlider | | TSpinEdit | QSpinBox | | TFloatSpinEdit | QDoubleSpinBox | | TListView | QTreeWidget/QListWidget | | TTreeView | QAbstractScrollArea | | TStatusBar | QStatusBar | | TToolBar | QToolBar | +----------------+-------------------------+ (Note: QTreeWidget is used only for TListView with ViewSTyle=vsReport other styles use QListWidget) +--------------+-----------------+----------------------------------+ | LCL Control | Qt equivalent | Description | +--------------+-----------------+----------------------------------+ | TMainMenu | QMenuBar | The horizontal menu at the top. | | TPopupMenu | QMenu | Right-click context menus. | | TSpeedButton | -- n/a -- | Qt is not used to render these. | | TBitBtn | QPushButton | Button with an icon/glyph. | | TScrollBar | QScrollBar | Standalone scroll control. | +--------------+-----------------+----------------------------------+ Beyond the main controls, qss also uses a concept of sub-controls. For instance: * QComboBox::drop-down - The little arrow button on the right of a dropdown. * QScrollBar::handle - The actual thing the user drags. * QTabWidget::pane - The border area around the tab contents. * QProgressBar::chunk - The actual colored "fill" of the progress bar. ========================+============================================ Example qss stylesheet: | ------------------------' /**************** * TPageControl * ****************/ QTabWidget::pane { border: 1px solid #639d74; top: -1px; } QTabBar::tab { background: #333; color: #83bd94; padding: 10px; border: 1px solid #639d74; border-top-right-radius: 12px; } QTabBar::tab:selected { background: #000; color: #83bd94; border-bottom: 0; } /*********************** * TList, TMemo, TEdit * ***********************/ QListWidget, QTextEdit, QLineEdit { background: #333; color: #639d74; border-radius: 12px; border: 1px solid #639d74; } QTextEdit:focus, QLineEdit:focus { border: 2px solid #83bd94; } /*********** * TPanel * ***********/ QFrame { border: 0; } /*********** * TButton * ***********/ QPushButton { background-color: qlineargradient( x1:0, y1:0, x2:0, y2:1, stop:0 #3e3e3e, stop:1 #2d2d2d ); border: 2px solid #8ba355; border-radius: 4px; color: #e0e0e0; font-family: serif; font-size: 14px; font-weight: normal; padding: 6px 15px; } QPushButton:hover { background-color: #4a4a4a; border: 2px solid #c5f059; color: #ffffff; } QPushButton#confirmButton { background-color: qlineargradient( x1:0, y1:0, x2:0, y2:1, stop:0 #2d3e2d, stop:1 #1e291e ); border: 2px solid #558b55; color: #c8e6c9; } QPushButton#confirmButton:hover { background-color: #364f36; border: 2px solid #7cb37c; color: #ffffff; } QPushButton#dangerButton { background-color: qlineargradient( x1:0, y1:0, x2:0, y2:1, stop:0 #4e2a2a, stop:1 #331a1a ); border: 2px solid #8b5555; color: #ffcdd2; } QPushButton#dangerButton:hover { background-color: #5f3333; border: 2px solid #b37c7c; color: #ffffff; } QPushButton:pressed { background-color: #1a1a1a; border-style: inset; padding-left: 9px; padding-top: 9px; } QPushButton:disabled { background-color: #252525; border: 2px solid #444444; color: #666666; } ===================================================================== Automatically setting control names. As mentioned earlier, one could presumably automate the synchronization between Qt control names and LCL control names, so below are some functions which do exactly that. Getting the order of operations right can be a bit tricky at times. Be sure all controls are fully instantiated before setting their names. Order of operations should be: * Instantiate controls * Name controls * Load stylesheet. procedure SetControlQtName(AControl: TWinControl); var QtWrapper: TQtWidget; NativeHandle: QObjectH; WS: WideString; begin if not AControl.HandleAllocated then AControl.HandleNeeded; QtWrapper := TQtWidget(AControl.Handle); NativeHandle := QObjectH(QtWrapper.Widget); WS := WideString(AControl.Name); QObject_setObjectName(NativeHandle, @WS); end; procedure SyncQtNames(Window: TWinControl); var i: Integer; Child: TControl; begin for i := 0 to Window.ControlCount - 1 do begin Child := Window.Controls[i]; if Child is TWinControl then begin SetControlQtName(TWinControl(Child)); SyncQtNames(TWinControl(Child)); end; end; end; procedure TMainWindowForm.Loaded; begin inherited Loaded; SyncQtNames(Self); end;