1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-06 05:25:41 -05:00
Files
DankMaterialShell/Modules/Settings/AppearanceTab.qml
2025-08-08 16:05:10 -04:00

973 lines
32 KiB
QML

import QtQuick
import QtQuick.Controls
import Quickshell.Io
import qs.Common
import qs.Services
import qs.Widgets
Item {
id: appearanceTab
DankFlickable {
anchors.fill: parent
anchors.topMargin: Theme.spacingL
anchors.bottomMargin: Theme.spacingXL
clip: true
contentHeight: mainColumn.height
contentWidth: width
Column {
id: mainColumn
width: parent.width
spacing: Theme.spacingXL
Loader {
width: parent.width
sourceComponent: displayComponent
}
Loader {
width: parent.width
sourceComponent: transparencyComponent
}
Loader {
width: parent.width
sourceComponent: cornerRadiusComponent
}
Loader {
width: parent.width
sourceComponent: themeComponent
}
Loader {
width: parent.width
sourceComponent: systemThemingComponent
}
}
}
// Display Settings Component
Component {
id: displayComponent
StyledRect {
width: parent.width
height: displaySection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g,
Theme.surfaceVariant.b, 0.3)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.2)
border.width: 1
Column {
id: displaySection
anchors.fill: parent
anchors.margins: Theme.spacingL
spacing: Theme.spacingM
Row {
width: parent.width
spacing: Theme.spacingM
DankIcon {
name: "monitor"
size: Theme.iconSize
color: Theme.primary
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: "Display Settings"
font.pixelSize: Theme.fontSizeLarge
font.weight: Font.Medium
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
}
DankToggle {
width: parent.width
text: "Night Mode"
description: "Apply warm color temperature to reduce eye strain"
checked: SettingsData.nightModeEnabled
onToggled: checked => {
SettingsData.setNightModeEnabled(checked)
if (checked)
nightModeEnableProcess.running = true
else
nightModeDisableProcess.running = true
}
}
DankToggle {
width: parent.width
text: "Light Mode"
description: "Use light theme instead of dark theme"
checked: SessionData.isLightMode
onToggled: checked => {
SessionData.setLightMode(checked)
Theme.isLightMode = checked
PortalService.setLightMode(checked)
}
}
DankDropdown {
width: parent.width
text: "Icon Theme"
description: "Select icon theme"
currentValue: SettingsData.iconTheme
enableFuzzySearch: true
popupWidthOffset: 100
maxPopupHeight: 400
options: {
SettingsData.detectAvailableIconThemes()
return SettingsData.availableIconThemes
}
onValueChanged: value => {
SettingsData.setIconTheme(value)
if (value !== "System Default"
&& !SettingsData.qt5ctAvailable
&& !SettingsData.qt6ctAvailable)
ToastService.showWarning(
"qt5ct or qt6ct not found - Qt app themes may not update without these tools")
}
}
DankDropdown {
width: parent.width
text: "Font Family"
description: "Select system font family"
currentValue: {
if (SettingsData.fontFamily === SettingsData.defaultFontFamily)
return "Default (" + SettingsData.defaultFontFamily + ")"
else
return SettingsData.fontFamily
|| "Default (" + SettingsData.defaultFontFamily + ")"
}
enableFuzzySearch: true
popupWidthOffset: 100
maxPopupHeight: 400
options: {
var fonts = ["Default (" + SettingsData.defaultFontFamily + ")"]
var availableFonts = Qt.fontFamilies()
var rootFamilies = []
var seenFamilies = new Set()
for (var i = 0; i < availableFonts.length; i++) {
var fontName = availableFonts[i]
if (fontName.startsWith("."))
continue
if (fontName === SettingsData.defaultFontFamily)
continue
var rootName = fontName.replace(
/ (Thin|Extra Light|Light|Regular|Medium|Semi Bold|Demi Bold|Bold|Extra Bold|Black|Heavy)$/i,
"").replace(
/ (Italic|Oblique|Condensed|Extended|Narrow|Wide)$/i,
"").replace(/ (UI|Display|Text|Mono|Sans|Serif)$/i,
function (match, suffix) {
return match
}).trim()
if (!seenFamilies.has(rootName) && rootName !== "") {
seenFamilies.add(rootName)
rootFamilies.push(rootName)
}
}
return fonts.concat(rootFamilies.sort())
}
onValueChanged: value => {
if (value.startsWith("Default ("))
SettingsData.setFontFamily(
SettingsData.defaultFontFamily)
else
SettingsData.setFontFamily(value)
}
}
DankDropdown {
width: parent.width
text: "Font Weight"
description: "Select font weight"
currentValue: {
switch (SettingsData.fontWeight) {
case Font.Thin:
return "Thin"
case Font.ExtraLight:
return "Extra Light"
case Font.Light:
return "Light"
case Font.Normal:
return "Regular"
case Font.Medium:
return "Medium"
case Font.DemiBold:
return "Demi Bold"
case Font.Bold:
return "Bold"
case Font.ExtraBold:
return "Extra Bold"
case Font.Black:
return "Black"
default:
return "Regular"
}
}
options: ["Thin", "Extra Light", "Light", "Regular", "Medium", "Demi Bold", "Bold", "Extra Bold", "Black"]
onValueChanged: value => {
var weight
switch (value) {
case "Thin":
weight = Font.Thin
break
case "Extra Light":
weight = Font.ExtraLight
break
case "Light":
weight = Font.Light
break
case "Regular":
weight = Font.Normal
break
case "Medium":
weight = Font.Medium
break
case "Demi Bold":
weight = Font.DemiBold
break
case "Bold":
weight = Font.Bold
break
case "Extra Bold":
weight = Font.ExtraBold
break
case "Black":
weight = Font.Black
break
default:
weight = Font.Normal
break
}
SettingsData.setFontWeight(weight)
}
}
DankDropdown {
width: parent.width
text: "Monospace Font"
description: "Select monospace font for process list and technical displays"
currentValue: {
if (SettingsData.monoFontFamily === SettingsData.defaultMonoFontFamily)
return "Default"
return SettingsData.monoFontFamily || "Default"
}
enableFuzzySearch: true
popupWidthOffset: 100
maxPopupHeight: 400
options: {
var fonts = ["Default"]
var availableFonts = Qt.fontFamilies()
var monoFamilies = []
var seenFamilies = new Set()
for (var i = 0; i < availableFonts.length; i++) {
var fontName = availableFonts[i]
if (fontName.startsWith("."))
continue
if (fontName === SettingsData.defaultMonoFontFamily)
continue
var lowerName = fontName.toLowerCase()
if (lowerName.includes("mono") || lowerName.includes(
"code") || lowerName.includes(
"console") || lowerName.includes(
"terminal") || lowerName.includes(
"courier") || lowerName.includes(
"dejavu sans mono") || lowerName.includes(
"jetbrains") || lowerName.includes(
"fira") || lowerName.includes("hack") || lowerName.includes(
"source code") || lowerName.includes(
"ubuntu mono") || lowerName.includes("cascadia")) {
var rootName = fontName.replace(
/ (Thin|Extra Light|Light|Regular|Medium|Semi Bold|Demi Bold|Bold|Extra Bold|Black|Heavy)$/i,
"").replace(
/ (Italic|Oblique|Condensed|Extended|Narrow|Wide)$/i,
"").trim()
if (!seenFamilies.has(rootName) && rootName !== "") {
seenFamilies.add(rootName)
monoFamilies.push(rootName)
}
}
}
return fonts.concat(monoFamilies.sort())
}
onValueChanged: value => {
if (value === "Default")
SettingsData.setMonoFontFamily(
SettingsData.defaultMonoFontFamily)
else
SettingsData.setMonoFontFamily(value)
}
}
}
}
}
// Transparency Settings Component
Component {
id: transparencyComponent
StyledRect {
width: parent.width
height: transparencySection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g,
Theme.surfaceVariant.b, 0.3)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.2)
border.width: 1
Column {
id: transparencySection
anchors.fill: parent
anchors.margins: Theme.spacingL
spacing: Theme.spacingM
Row {
width: parent.width
spacing: Theme.spacingM
DankIcon {
name: "opacity"
size: Theme.iconSize
color: Theme.primary
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: "Transparency Settings"
font.pixelSize: Theme.fontSizeLarge
font.weight: Font.Medium
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
}
Column {
width: parent.width
spacing: Theme.spacingS
StyledText {
text: "Top Bar Transparency"
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
font.weight: Font.Medium
}
DankSlider {
width: parent.width
height: 24
value: Math.round(SettingsData.topBarTransparency * 100)
minimum: 0
maximum: 100
unit: ""
showValue: true
onSliderValueChanged: newValue => {
SettingsData.setTopBarTransparency(
newValue / 100)
}
}
}
Column {
width: parent.width
spacing: Theme.spacingS
StyledText {
text: "Top Bar Widget Transparency"
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
font.weight: Font.Medium
}
DankSlider {
width: parent.width
height: 24
value: Math.round(SettingsData.topBarWidgetTransparency * 100)
minimum: 0
maximum: 100
unit: ""
showValue: true
onSliderValueChanged: newValue => {
SettingsData.setTopBarWidgetTransparency(
newValue / 100)
}
}
}
Column {
width: parent.width
spacing: Theme.spacingS
StyledText {
text: "Popup Transparency"
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
font.weight: Font.Medium
}
DankSlider {
width: parent.width
height: 24
value: Math.round(SettingsData.popupTransparency * 100)
minimum: 0
maximum: 100
unit: ""
showValue: true
onSliderValueChanged: newValue => {
SettingsData.setPopupTransparency(
newValue / 100)
}
}
}
}
}
}
// Theme Color Component
Component {
id: themeComponent
StyledRect {
width: parent.width
height: themeSection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g,
Theme.surfaceVariant.b, 0.3)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.2)
border.width: 1
Column {
id: themeSection
anchors.fill: parent
anchors.margins: Theme.spacingL
spacing: Theme.spacingM
Row {
width: parent.width
spacing: Theme.spacingM
DankIcon {
name: "palette"
size: Theme.iconSize
color: Theme.primary
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: "Theme Color"
font.pixelSize: Theme.fontSizeLarge
font.weight: Font.Medium
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
}
Column {
width: parent.width
spacing: Theme.spacingS
StyledText {
text: "Current Theme: " + (Theme.isDynamicTheme ? "Auto" : (Theme.currentThemeIndex < Theme.themes.length ? Theme.themes[Theme.currentThemeIndex].name : "Blue"))
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
font.weight: Font.Medium
anchors.horizontalCenter: parent.horizontalCenter
}
StyledText {
text: {
if (Theme.isDynamicTheme)
return "Wallpaper-based dynamic colors"
var descriptions = ["Material blue inspired by modern interfaces", "Deep blue inspired by material 3", "Rich purple tones for BB elegance", "Natural green for productivity", "Energetic orange for creativity", "Bold red for impact", "Cool cyan for tranquility", "Vibrant pink for expression", "Warm amber for comfort", "Soft coral for gentle warmth"]
return descriptions[Theme.currentThemeIndex] || "Select a theme"
}
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
anchors.horizontalCenter: parent.horizontalCenter
wrapMode: Text.WordWrap
width: Math.min(parent.width, 400)
horizontalAlignment: Text.AlignHCenter
}
}
Column {
spacing: Theme.spacingS
anchors.horizontalCenter: parent.horizontalCenter
Row {
spacing: Theme.spacingM
anchors.horizontalCenter: parent.horizontalCenter
Repeater {
model: 5
Rectangle {
width: 32
height: 32
radius: 16
color: Theme.themes[index].primary
border.color: Theme.outline
border.width: (Theme.currentThemeIndex === index
&& !Theme.isDynamicTheme) ? 2 : 1
scale: (Theme.currentThemeIndex === index
&& !Theme.isDynamicTheme) ? 1.1 : 1
Rectangle {
width: nameText.contentWidth + Theme.spacingS * 2
height: nameText.contentHeight + Theme.spacingXS * 2
color: Theme.surfaceContainer
border.color: Theme.outline
border.width: 1
radius: Theme.cornerRadius
anchors.bottom: parent.top
anchors.bottomMargin: Theme.spacingXS
anchors.horizontalCenter: parent.horizontalCenter
visible: mouseArea.containsMouse
StyledText {
id: nameText
text: Theme.themes[index].name
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
anchors.centerIn: parent
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
Theme.switchTheme(index, false)
}
}
Behavior on scale {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.emphasizedEasing
}
}
Behavior on border.width {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.emphasizedEasing
}
}
}
}
}
Row {
spacing: Theme.spacingM
anchors.horizontalCenter: parent.horizontalCenter
Repeater {
model: 5
Rectangle {
property int themeIndex: index + 5
width: 32
height: 32
radius: 16
color: themeIndex
< Theme.themes.length ? Theme.themes[themeIndex].primary : "transparent"
border.color: Theme.outline
border.width: Theme.currentThemeIndex === themeIndex ? 2 : 1
visible: themeIndex < Theme.themes.length
scale: Theme.currentThemeIndex === themeIndex ? 1.1 : 1
Rectangle {
width: nameText2.contentWidth + Theme.spacingS * 2
height: nameText2.contentHeight + Theme.spacingXS * 2
color: Theme.surfaceContainer
border.color: Theme.outline
border.width: 1
radius: Theme.cornerRadius
anchors.bottom: parent.top
anchors.bottomMargin: Theme.spacingXS
anchors.horizontalCenter: parent.horizontalCenter
visible: mouseArea2.containsMouse
&& themeIndex < Theme.themes.length
StyledText {
id: nameText2
text: themeIndex < Theme.themes.length ? Theme.themes[themeIndex].name : ""
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
anchors.centerIn: parent
}
}
MouseArea {
id: mouseArea2
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
if (themeIndex < Theme.themes.length)
Theme.switchTheme(themeIndex)
}
}
Behavior on scale {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.emphasizedEasing
}
}
Behavior on border.width {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.emphasizedEasing
}
}
}
}
}
Item {
width: 1
height: Theme.spacingM
}
Rectangle {
width: 120
height: 40
radius: 20
anchors.horizontalCenter: parent.horizontalCenter
color: {
if (ToastService.wallpaperErrorStatus === "error"
|| ToastService.wallpaperErrorStatus === "matugen_missing")
return Qt.rgba(Theme.error.r, Theme.error.g,
Theme.error.b, 0.12)
else
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g,
Theme.surfaceVariant.b, 0.3)
}
border.color: {
if (ToastService.wallpaperErrorStatus === "error"
|| ToastService.wallpaperErrorStatus === "matugen_missing")
return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.5)
else if (Theme.isDynamicTheme)
return Theme.primary
else
return Theme.outline
}
border.width: Theme.isDynamicTheme ? 2 : 1
scale: Theme.isDynamicTheme ? 1.1 : (autoMouseArea.containsMouse ? 1.02 : 1)
Row {
anchors.centerIn: parent
spacing: Theme.spacingS
DankIcon {
name: {
if (ToastService.wallpaperErrorStatus === "error"
|| ToastService.wallpaperErrorStatus === "matugen_missing")
return "error"
else
return "palette"
}
size: 16
color: {
if (ToastService.wallpaperErrorStatus === "error"
|| ToastService.wallpaperErrorStatus === "matugen_missing")
return Theme.error
else
return Theme.surfaceText
}
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: {
if (ToastService.wallpaperErrorStatus === "error")
return "Error"
else if (ToastService.wallpaperErrorStatus === "matugen_missing")
return "No matugen"
else
return "Auto"
}
font.pixelSize: Theme.fontSizeMedium
color: {
if (ToastService.wallpaperErrorStatus === "error"
|| ToastService.wallpaperErrorStatus === "matugen_missing")
return Theme.error
else
return Theme.surfaceText
}
font.weight: Font.Medium
anchors.verticalCenter: parent.verticalCenter
}
}
MouseArea {
id: autoMouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
if (ToastService.wallpaperErrorStatus === "matugen_missing")
ToastService.showError(
"matugen not found - install matugen package for dynamic theming")
else if (ToastService.wallpaperErrorStatus === "error")
ToastService.showError(
"Wallpaper processing failed - check wallpaper path")
else
Theme.switchTheme(10, true)
}
}
Rectangle {
width: autoTooltipText.contentWidth + Theme.spacingM * 2
height: autoTooltipText.contentHeight + Theme.spacingS * 2
color: Theme.surfaceContainer
border.color: Theme.outline
border.width: 1
radius: Theme.cornerRadius
anchors.bottom: parent.top
anchors.bottomMargin: Theme.spacingS
anchors.horizontalCenter: parent.horizontalCenter
visible: autoMouseArea.containsMouse
&& (!Theme.isDynamicTheme
|| ToastService.wallpaperErrorStatus === "error"
|| ToastService.wallpaperErrorStatus === "matugen_missing")
StyledText {
id: autoTooltipText
text: {
if (ToastService.wallpaperErrorStatus === "matugen_missing")
return "Install matugen package for dynamic themes"
else
return "Dynamic wallpaper-based colors"
}
font.pixelSize: Theme.fontSizeSmall
color: (ToastService.wallpaperErrorStatus === "error"
|| ToastService.wallpaperErrorStatus
=== "matugen_missing") ? Theme.error : Theme.surfaceText
anchors.centerIn: parent
wrapMode: Text.WordWrap
width: Math.min(implicitWidth, 250)
horizontalAlignment: Text.AlignHCenter
}
}
Behavior on scale {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.emphasizedEasing
}
}
Behavior on color {
ColorAnimation {
duration: Theme.mediumDuration
easing.type: Theme.standardEasing
}
}
Behavior on border.color {
ColorAnimation {
duration: Theme.mediumDuration
easing.type: Theme.standardEasing
}
}
}
}
}
}
}
// Corner Radius Component
Component {
id: cornerRadiusComponent
StyledRect {
width: parent.width
height: cornerRadiusSection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g,
Theme.surfaceVariant.b, 0.3)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.2)
border.width: 1
Column {
id: cornerRadiusSection
anchors.fill: parent
anchors.margins: Theme.spacingL
spacing: Theme.spacingM
Row {
width: parent.width
spacing: Theme.spacingM
DankIcon {
name: "rounded_corner"
size: Theme.iconSize
color: Theme.primary
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: "Corner Radius"
font.pixelSize: Theme.fontSizeLarge
font.weight: Font.Medium
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
}
Column {
width: parent.width
spacing: Theme.spacingS
StyledText {
text: "Bar & Widget Corner Roundness"
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
font.weight: Font.Medium
}
DankSlider {
width: parent.width
height: 24
value: SettingsData.cornerRadius
minimum: 0
maximum: 32
unit: ""
showValue: true
onSliderValueChanged: newValue => {
SettingsData.setCornerRadius(newValue)
}
}
}
}
}
}
// System App Theming Component
Component {
id: systemThemingComponent
StyledRect {
width: parent.width
height: systemThemingSection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g,
Theme.surfaceVariant.b, 0.3)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.2)
border.width: 1
visible: Theme.isDynamicTheme && Colors.matugenAvailable
Column {
id: systemThemingSection
anchors.fill: parent
anchors.margins: Theme.spacingL
spacing: Theme.spacingM
Row {
width: parent.width
spacing: Theme.spacingM
DankIcon {
name: "extension"
size: Theme.iconSize
color: Theme.primary
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: "System App Theming"
font.pixelSize: Theme.fontSizeLarge
font.weight: Font.Medium
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
}
DankToggle {
width: parent.width
text: "Theme GTK Applications"
description: Colors.gtkThemingEnabled ? "File managers, text editors, and system dialogs will match your theme" : "GTK theming not available (install gsettings)"
enabled: Colors.gtkThemingEnabled
checked: Colors.gtkThemingEnabled && SettingsData.gtkThemingEnabled
onToggled: function (checked) {
SettingsData.setGtkThemingEnabled(checked)
if (checked && Theme.isDynamicTheme)
Colors.generateGtkThemes()
}
}
DankToggle {
width: parent.width
text: "Theme Qt Applications"
description: Colors.qtThemingEnabled ? "Qt applications will match your theme colors" : "Qt theming not available (install qt5ct or qt6ct)"
enabled: Colors.qtThemingEnabled
checked: Colors.qtThemingEnabled && SettingsData.qtThemingEnabled
onToggled: function (checked) {
SettingsData.setQtThemingEnabled(checked)
if (checked && Theme.isDynamicTheme)
Colors.generateQtThemes()
}
}
}
}
}
Process {
id: nightModeEnableProcess
command: ["bash", "-c", "if command -v wlsunset > /dev/null; then pkill wlsunset; wlsunset -t 3000 & elif command -v redshift > /dev/null; then pkill redshift; redshift -P -O 3000 & else echo 'No night mode tool available'; fi"]
running: false
onExited: exitCode => {
if (exitCode !== 0)
SettingsData.setNightModeEnabled(true)
}
}
Process {
id: nightModeDisableProcess
command: ["bash", "-c", "pkill wlsunset; pkill redshift; if command -v wlsunset > /dev/null; then wlsunset -t 6500 -T 6500 & sleep 1; pkill wlsunset; elif command -v redshift > /dev/null; then redshift -P -O 6500; redshift -x; fi"]
running: false
onExited: exitCode => {
if (exitCode !== 0)
SettingsData.setNightModeEnabled(false)
}
}
}