Bar Widget Development
Bar widgets are components that appear in the top or bottom bar of Noctalia Shell. They provide quick access to information and actions.
Basic Structure
Section titled “Basic Structure”A bar widget is a QML file that must follow this structure:
import QtQuickimport QtQuick.Layoutsimport Quickshellimport qs.Commonsimport qs.Widgets
Rectangle { id: root
// Plugin API (injected by PluginService) property var pluginApi: null
// Required properties for bar widgets property ShellScreen screen property string widgetId: "" property string section: ""
// Widget dimensions implicitWidth: content.implicitWidth + Style.marginM * 2 implicitHeight: Style.barHeight
// Your widget content here}Required Properties
Section titled “Required Properties”pluginApi
Section titled “pluginApi”Injected by the PluginService. Provides access to plugin APIs and services.
property var pluginApi: nullscreen
Section titled “screen”The ShellScreen this widget is displayed on (for multi-monitor support).
property ShellScreen screenwidgetId
Section titled “widgetId”Unique identifier for this widget instance.
property string widgetId: ""section
Section titled “section”The bar section this widget is in: "left", "center", or "right".
property string section: ""Sizing
Section titled “Sizing”Bar widgets should set their size appropriately:
// Horizontal bar (top/bottom)implicitWidth: content.implicitWidth + Style.marginM * 2implicitHeight: Style.barHeight
// Vertical bar (left/right) - adapt to bar positionreadonly property string barPosition: Settings.data.bar.positionreadonly property bool isVertical: barPosition === "left" || barPosition === "right"
implicitWidth: isVertical ? Style.capsuleHeight : content.implicitWidth + Style.marginM * 2implicitHeight: isVertical ? content.implicitHeight + Style.marginM * 2 : Style.capsuleHeightStyling
Section titled “Styling”Use Noctalia’s built-in styling system for consistency:
Colors
Section titled “Colors”import qs.Commons
Rectangle { // Surface colors color: Color.mSurface color: Color.mSurfaceVariant color: Style.capsuleColor // Recommended for bar widgets
// Text colors NText { color: Color.mOnSurface color: Color.mOnSurfaceVariant color: Color.mPrimary }}Spacing and Sizes
Section titled “Spacing and Sizes”import qs.Commons
ColumnLayout { spacing: Style.marginS // Small spacing spacing: Style.marginM // Medium spacing spacing: Style.marginL // Large spacing
NIcon { pointSize: Style.fontSizeS // Small icon pointSize: Style.fontSizeM // Medium icon pointSize: Style.fontSizeL // Large icon }}
Rectangle { radius: Style.radiusS // Small radius radius: Style.radiusM // Medium radius (recommended) radius: Style.radiusL // Large radius}Typography
Section titled “Typography”NText { pointSize: Style.fontSizeXS pointSize: Style.fontSizeS pointSize: Style.fontSizeM pointSize: Style.fontSizeL font.weight: Font.Light font.weight: Font.Normal font.weight: Font.Medium font.weight: Font.Bold}Using Settings
Section titled “Using Settings”Access plugin settings via pluginApi:
// Get setting with fallback to defaultreadonly property string message: pluginApi?.pluginSettings?.message || pluginApi?.manifest?.metadata?.defaultSettings?.message || "Default Message"
// Update and save settingfunction updateMessage(newMessage) { pluginApi.pluginSettings.message = newMessage pluginApi.saveSettings()}Interactions
Section titled “Interactions”Click Events
Section titled “Click Events”MouseArea { anchors.fill: parent cursorShape: Qt.PointingHandCursor
onClicked: { // Open plugin panel pluginApi.openPanel(root.screen)
// Or perform action performAction() }}Hover Effects
Section titled “Hover Effects”MouseArea { anchors.fill: parent hoverEnabled: true
onEntered: { root.color = Qt.lighter(root.color, 1.1) }
onExited: { root.color = Style.capsuleColor }}Tooltips
Section titled “Tooltips”import qs.Services.UIimport qs.Services.System
MouseArea { anchors.fill: parent hoverEnabled: true
onEntered: { TooltipService.show(root, "Widget tooltip text", BarService.getTooltipDirection()) }
onExited: { TooltipService.hide() }}You can also use a function to build dynamic tooltip content:
function buildTooltip() { return "Status: " + (enabled ? "Active" : "Inactive")}
MouseArea { anchors.fill: parent hoverEnabled: true
onEntered: { TooltipService.show(root, buildTooltip(), BarService.getTooltipDirection()) }
onExited: { TooltipService.hide() }}The BarService.getTooltipDirection() function automatically determines the correct tooltip direction based on the bar’s position (top, bottom, left, or right).
Opening Panels
Section titled “Opening Panels”If your plugin provides a panel, open it from the widget:
MouseArea { anchors.fill: parent onClicked: { if (pluginApi) { pluginApi.openPanel(root.screen) } }}Using Noctalia Widgets
Section titled “Using Noctalia Widgets”Noctalia provides many pre-built widgets:
import qs.Widgets
NIcon { icon: "heart" // Tabler icon name color: Color.mPrimary applyUiScale: true // Automatically scale with UI}NText { text: "Hello World" color: Color.mOnSurface pointSize: Style.fontSizeM font.weight: Font.Medium}Buttons
Section titled “Buttons”NIconButton { icon: "settings" onClicked: { // Handle click }}Accessing Services
Section titled “Accessing Services”Plugins can use Noctalia services:
Toast Notifications
Section titled “Toast Notifications”import qs.Services.UI
ToastService.showNotice("Success message")ToastService.showError("Error message")Logging
Section titled “Logging”import qs.Commons
Component.onCompleted: { Logger.i("MyPlugin", "Widget loaded") Logger.d("MyPlugin", "Debug info:", someValue) Logger.w("MyPlugin", "Warning message") Logger.e("MyPlugin", "Error occurred")}Settings Access
Section titled “Settings Access”import qs.Commons
// Access global Noctalia settingsreadonly property string barPosition: Settings.data.bar.positionreadonly property bool isDarkMode: Settings.data.ui.darkModeComplete Examples
Section titled “Complete Examples”Simple Status Widget
Section titled “Simple Status Widget”import QtQuickimport QtQuick.Layoutsimport Quickshellimport qs.Commonsimport qs.Widgets
Rectangle { id: root
property var pluginApi: null property ShellScreen screen property string widgetId: "" property string section: ""
implicitWidth: row.implicitWidth + Style.marginM * 2 implicitHeight: Style.barHeight
color: Style.capsuleColor radius: Style.radiusM
RowLayout { id: row anchors.centerIn: parent spacing: Style.marginS
NIcon { icon: "check" color: Color.mPrimary }
NText { text: "Ready" color: Color.mOnSurface pointSize: Style.fontSizeS } }}Interactive Counter Widget
Section titled “Interactive Counter Widget”import QtQuickimport QtQuick.Layoutsimport Quickshellimport qs.Commonsimport qs.Widgetsimport qs.Services.UI
Rectangle { id: root
property var pluginApi: null property ShellScreen screen property string widgetId: "" property string section: ""
property int count: pluginApi?.pluginSettings?.count || 0
implicitWidth: row.implicitWidth + Style.marginM * 2 implicitHeight: Style.barHeight
color: Style.capsuleColor radius: Style.radiusM
RowLayout { id: row anchors.centerIn: parent spacing: Style.marginS
NIcon { icon: "numbers" color: Color.mPrimary }
NText { text: root.count.toString() color: Color.mOnSurface pointSize: Style.fontSizeM font.weight: Font.Bold } }
MouseArea { anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor
onEntered: { root.color = Qt.lighter(Style.capsuleColor, 1.1) }
onExited: { root.color = Style.capsuleColor }
onClicked: { root.count++ pluginApi.pluginSettings.count = root.count pluginApi.saveSettings() ToastService.showNotice("Count: " + root.count) } }}Widget with Panel
Section titled “Widget with Panel”import QtQuickimport QtQuick.Layoutsimport Quickshellimport qs.Commonsimport qs.Widgets
Rectangle { id: root
property var pluginApi: null property ShellScreen screen property string widgetId: "" property string section: ""
readonly property string message: pluginApi?.pluginSettings?.message || pluginApi?.manifest?.metadata?.defaultSettings?.message || ""
implicitWidth: row.implicitWidth + Style.marginM * 2 implicitHeight: Style.barHeight
color: Style.capsuleColor radius: Style.radiusM
RowLayout { id: row anchors.centerIn: parent spacing: Style.marginS
NIcon { icon: "noctalia" }
NText { text: root.message color: Color.mOnSurface pointSize: Style.fontSizeS } }
MouseArea { anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor
onEntered: { root.color = Qt.lighter(Style.capsuleColor, 1.1) }
onExited: { root.color = Style.capsuleColor }
onClicked: { if (pluginApi) { pluginApi.openPanel(root.screen) } } }}Vertical Bar Support
Section titled “Vertical Bar Support”Support vertical bars (left/right positions):
import QtQuickimport QtQuick.Layoutsimport Quickshellimport qs.Commonsimport qs.Widgets
Rectangle { id: root
property var pluginApi: null property ShellScreen screen property string widgetId: "" property string section: ""
readonly property string barPosition: Settings.data.bar.position readonly property bool isVertical: barPosition === "left" || barPosition === "right"
implicitWidth: isVertical ? Style.capsuleHeight : layout.implicitWidth + Style.marginM * 2 implicitHeight: isVertical ? layout.implicitHeight + Style.marginM * 2 : Style.capsuleHeight
color: Style.capsuleColor radius: Style.radiusM
Item { id: layout anchors.centerIn: parent implicitWidth: rowLayout.visible ? rowLayout.implicitWidth : colLayout.implicitWidth implicitHeight: rowLayout.visible ? rowLayout.implicitHeight : colLayout.implicitHeight
RowLayout { id: rowLayout visible: !root.isVertical spacing: Style.marginS
NIcon { icon: "heart" color: Color.mPrimary }
NText { text: "Widget" color: Color.mOnSurface } }
ColumnLayout { id: colLayout visible: root.isVertical spacing: Style.marginS
NIcon { icon: "heart" color: Color.mPrimary }
NText { text: "Widget" color: Color.mOnSurface } } }}Best Practices
Section titled “Best Practices”- Keep it small: Bar widgets should be compact and unobtrusive
- Use consistent styling: Stick to Noctalia’s design system
- Handle missing data: Always provide fallbacks for settings and data
- Respect dark mode: Use
Color.m*colors that adapt to theme - Add hover effects: Provide visual feedback for interactive elements
- Log important events: Use Logger for debugging
- Test on vertical bars: Ensure your widget works in all bar positions
- Optimize performance: Avoid expensive operations in the widget
See Also
Section titled “See Also”- Getting Started - Create your first plugin
- Panel Development - Create overlay panels
- Plugin API - Full API reference
- Manifest Reference - Plugin configuration
Coming Soon
- Settings UI - Add configuration UI
- Styling Guide - Design system details