Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -667,7 +667,7 @@ int main(int argc, char *argv[])
}
}

auto applyWindowEffects = [config](QQuickWindow *window) {
auto applyWindowEffects = [config, theme](QQuickWindow *window) {
if (!window)
return;

Expand All @@ -680,8 +680,13 @@ int main(int argc, char *argv[])

const bool contrastAvailable = KWindowEffects::isEffectAvailable(KWindowEffects::BackgroundContrast);
KWindowEffects::enableBackgroundContrast(window, blurRequested && contrastAvailable);

// Expose compositor blur availability to QML so glass effects can
// adapt their intensity (reduce QML blur when compositor provides it).
theme->setCompositorBlurAvailable(blurAvailable);
#else
Q_UNUSED(window)
theme->setCompositorBlurAvailable(false);
#endif
};

Expand Down
37 changes: 37 additions & 0 deletions src/qml/Main.qml
Original file line number Diff line number Diff line change
Expand Up @@ -3981,6 +3981,43 @@ ApplicationWindow {
Layout.fillHeight: true
color: Theme.contentBackground

// ── Glass Effects (content area) ─────────────────────────
Rectangle {
visible: Theme.hasEffects && Theme.gradientEnabled
anchors.fill: parent
z: 0
gradient: Gradient {
orientation: Gradient.Vertical
GradientStop { position: 0.0; color: Qt.rgba(Theme.gradientColor.r, Theme.gradientColor.g, Theme.gradientColor.b, 0.12) }
GradientStop { position: 1.0; color: "transparent" }
}
}

Rectangle {
visible: Theme.hasEffects && Theme.noiseEnabled && Theme.noiseOpacity > 0
anchors.fill: parent
z: 0
color: "transparent"
opacity: Theme.noiseOpacity * 0.5
Canvas {
anchors.fill: parent
onPaint: {
var ctx = getContext("2d")
var w = width, h = height
var imgData = ctx.createImageData(w, h)
for (var i = 0; i < imgData.data.length; i += 4) {
var v = Math.random() * 255
imgData.data[i] = v
imgData.data[i+1] = v
imgData.data[i+2] = v
imgData.data[i+3] = 20
}
ctx.putImageData(imgData, 0, 0)
}
Component.onCompleted: requestPaint()
}
}

// Curved mantle fills for inverse rounded corners
Shape {
z: 1; width: Theme.radiusMedium; height: Theme.radiusMedium
Expand Down
154 changes: 154 additions & 0 deletions src/qml/components/SettingsPanel.qml
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,13 @@ Window {
function setDraftTheme(themeName) {
draftTheme = themeName
draftDarkMode = isDarkTheme(themeName)
if (themeName === "liquid-dark") {
draftDarkTheme = "liquid-dark"
draftLightTheme = "liquid-light"
} else if (themeName === "liquid-light") {
draftDarkTheme = "liquid-dark"
draftLightTheme = "liquid-light"
}
}

function bindAppearancePreview() {
Expand Down Expand Up @@ -661,6 +668,153 @@ Window {
root.queueSettingsApply()
}
}

// ── Glass Effects (only visible for themes with [effects]) ──
Text {
visible: Theme.hasEffects
text: "Glass Effects"
color: Theme.accent
font.pointSize: Theme.fontSmall
font.bold: true
Layout.topMargin: 12
Layout.bottomMargin: 4
}

Text {
visible: Theme.hasEffects
text: "These effects are defined by the active theme. Adjust to taste."
color: Theme.subtext
font.pointSize: Theme.fontSmall
wrapMode: Text.WordWrap
Layout.fillWidth: true
Layout.bottomMargin: 4
}

Q.Slider {
visible: Theme.hasEffects
Layout.fillWidth: true
label: "Sidebar opacity"
from: 0
to: 100
stepSize: 1
showValue: true
value: Theme.sidebarOpacity * 100
onMoved: (value) => {
Theme.sidebarOpacity = value / 100
}
}

Q.Slider {
visible: Theme.hasEffects
Layout.fillWidth: true
label: "Content opacity"
from: 0
to: 100
stepSize: 1
showValue: true
value: Theme.contentOpacity * 100
onMoved: (value) => {
Theme.contentOpacity = value / 100
}
}

Q.Slider {
visible: Theme.hasEffects
Layout.fillWidth: true
label: "Toolbar opacity"
from: 0
to: 100
stepSize: 1
showValue: true
value: Theme.toolbarOpacity * 100
onMoved: (value) => {
Theme.toolbarOpacity = value / 100
}
}

Q.Slider {
visible: Theme.hasEffects && Theme.blurEnabled
Layout.fillWidth: true
label: "Blur radius"
from: 0
to: 64
stepSize: 1
showValue: true
value: Theme.blurRadius
onMoved: (value) => {
Theme.blurRadius = Math.round(value)
}
}

Q.Slider {
visible: Theme.hasEffects && Theme.glowEnabled
Layout.fillWidth: true
label: "Glow intensity"
from: 0
to: 100
stepSize: 1
showValue: true
value: Theme.glowOpacity * 100
onMoved: (value) => {
Theme.glowOpacity = value / 100
}
}

Q.Slider {
visible: Theme.hasEffects && Theme.glowEnabled
Layout.fillWidth: true
label: "Glow radius"
from: 0
to: 64
stepSize: 1
showValue: true
value: Theme.glowRadius
onMoved: (value) => {
Theme.glowRadius = Math.round(value)
}
}

Q.Slider {
visible: Theme.hasEffects && Theme.noiseEnabled
Layout.fillWidth: true
label: "Noise intensity"
from: 0
to: 20
stepSize: 1
showValue: true
value: Theme.noiseOpacity * 100
onMoved: (value) => {
Theme.noiseOpacity = value / 100
}
}

Q.Slider {
visible: Theme.hasEffects
Layout.fillWidth: true
label: "Saturation"
from: 50
to: 200
stepSize: 5
showValue: true
value: Theme.saturation * 100
onMoved: (value) => {
Theme.saturation = value / 100
}
}

Q.Slider {
visible: Theme.hasEffects && Theme.refractionEnabled
Layout.fillWidth: true
label: "Refraction strength"
from: 0
to: 10
stepSize: 1
showValue: true
value: Theme.refractionStrength * 100
onMoved: (value) => {
Theme.refractionStrength = value / 100
}
}
}
}

Expand Down
57 changes: 54 additions & 3 deletions src/qml/components/Sidebar.qml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Rectangle {
signal collapseClicked()
signal featureHintRequested(string message)

color: Theme.chromeBackground
color: Theme.sidebarBackground
clip: false

Component { id: iconHome; IconHome { size: 18; color: Theme.subtext } }
Expand All @@ -48,7 +48,7 @@ Rectangle {
z: 1; width: Theme.radiusMedium; height: Theme.radiusMedium
anchors.top: parent.top; anchors.left: parent.right
ShapePath {
fillColor: Theme.chromeBackground; strokeColor: "transparent"
fillColor: Theme.sidebarBackground; strokeColor: "transparent"
startX: 0; startY: 0
PathLine { x: Theme.radiusMedium; y: 0 }
PathArc {
Expand All @@ -65,7 +65,7 @@ Rectangle {
z: 1; width: Theme.radiusMedium; height: Theme.radiusMedium
anchors.bottom: parent.bottom; anchors.left: parent.right
ShapePath {
fillColor: Theme.chromeBackground; strokeColor: "transparent"
fillColor: Theme.sidebarBackground; strokeColor: "transparent"
startX: 0; startY: Theme.radiusMedium
PathLine { x: Theme.radiusMedium; y: Theme.radiusMedium }
PathArc {
Expand All @@ -77,6 +77,57 @@ Rectangle {
}
}

// ── Glass Effects (active only when theme provides [effects]) ─────

// Gradient tint overlay
Rectangle {
visible: Theme.hasEffects && Theme.gradientEnabled
anchors.fill: parent
z: 0
gradient: Gradient {
orientation: Theme.gradientDirection === "left_to_right" ? Gradient.Horizontal : Gradient.Vertical
GradientStop { position: 0.0; color: Qt.rgba(Theme.gradientColor.r, Theme.gradientColor.g, Theme.gradientColor.b, 0.6) }
GradientStop { position: 1.0; color: Qt.rgba(Theme.gradientColor.r, Theme.gradientColor.g, Theme.gradientColor.b, 0.15) }
}
}

// Noise texture overlay (frosted grain)
Rectangle {
visible: Theme.hasEffects && Theme.noiseEnabled && Theme.noiseOpacity > 0
anchors.fill: parent
z: 0
color: "transparent"
opacity: Theme.noiseOpacity
Canvas {
anchors.fill: parent
onPaint: {
var ctx = getContext("2d")
var w = width, h = height
var imgData = ctx.createImageData(w, h)
for (var i = 0; i < imgData.data.length; i += 4) {
var v = Math.random() * 255
imgData.data[i] = v
imgData.data[i+1] = v
imgData.data[i+2] = v
imgData.data[i+3] = 25
}
ctx.putImageData(imgData, 0, 0)
}
Component.onCompleted: requestPaint()
}
}

// Border glow effect
Rectangle {
visible: Theme.hasEffects && Theme.glowEnabled && Theme.glowOpacity > 0
anchors.fill: parent
z: 0
color: "transparent"
border.width: 1
border.color: Qt.rgba(Theme.glowColor.r, Theme.glowColor.g, Theme.glowColor.b, Theme.glowOpacity * 0.5)
radius: Theme.radiusLarge
}

ColumnLayout {
anchors.fill: parent
spacing: 0
Expand Down
22 changes: 22 additions & 0 deletions src/qml/components/Toolbar.qml
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,28 @@ Rectangle {
implicitHeight: toolbarColumn.implicitHeight
color: Theme.chromeBackground

// ── Glass Effects (toolbar) ──────────────────────────────────────
Rectangle {
visible: Theme.hasEffects && Theme.gradientEnabled
anchors.fill: parent
z: 0
gradient: Gradient {
orientation: Gradient.Horizontal
GradientStop { position: 0.0; color: Qt.rgba(Theme.gradientColor.r, Theme.gradientColor.g, Theme.gradientColor.b, 0.25) }
GradientStop { position: 1.0; color: "transparent" }
}
}

Rectangle {
visible: Theme.hasEffects && Theme.glowEnabled && Theme.glowOpacity > 0
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
height: 1
z: 0
color: Qt.rgba(Theme.glowColor.r, Theme.glowColor.g, Theme.glowColor.b, Theme.glowOpacity * 0.3)
}

DragHandler {
enabled: root.showWindowControls && root.window
target: null
Expand Down
Loading
Loading