diff --git a/docs/THEMING.md b/docs/THEMING.md index 76833e4b..675513b1 100644 --- a/docs/THEMING.md +++ b/docs/THEMING.md @@ -4,14 +4,14 @@ A default theme from this repository can be copied to use as a base and edit, for example: ```sh - cp mirage/src/themes/Midnight.qpl \ + cp mirage/src/themes/Foliage.qpl \ "${XDG_DATA_HOME:-$HOME/.local/share}/mirage/themes/MyTheme.qpl" ``` Or for Flatpak users: ```sh - cp mirage/src/themes/Midnight.qpl \ + cp mirage/src/themes/Foliage.qpl \ ~/.var/app/io.github.mirukana.mirage/data/mirage/themes/MyTheme.qpl ``` diff --git a/src/backend/user_files.py b/src/backend/user_files.py index 28b51cbe..bbd34a94 100644 --- a/src/backend/user_files.py +++ b/src/backend/user_files.py @@ -522,10 +522,10 @@ class Theme(UserDataFile): @property def default_data(self) -> str: - if self.filename in ("Midnight.qpl", "Glass.qpl"): + if self.filename in ("Foliage.qpl", "Midnight.qpl", "Glass.qpl"): path = f"src/themes/{self.filename}" else: - path = "src/themes/Midnight.qpl" + path = "src/themes/Foliage.qpl" try: byte_content = pyotherside.qrc_get_file_contents(path) diff --git a/src/config/settings.py b/src/config/settings.py index ce1abd46..1d622ecb 100644 --- a/src/config/settings.py +++ b/src/config/settings.py @@ -26,13 +26,14 @@ class General: tooltips_delay: float = 0.7 # Application theme to use. - # Can be the name of a built-in theme (Mirage.qpl or Glass.qpl), or - # the name (including extension) of a file in the user theme folder, which - # is "$XDG_DATA_HOME/mirage/themes" if that environment variable is set, + # Can be the name of a built-in theme (Foliage.qpl, Midnight.qpl or + # Glass.qpl), or the name (including extension) of a file in the user theme + # folder, which is "$XDG_DATA_HOME/moment/themes" if that environment + # variable is set, # else "~/.local/share/moment/themes". # For Flatpak, it is # "~/.var/app/io.github.mirukana.mirage/data/mirage/themes". - theme: str = "Midnight.qpl" + theme: str = "Foliage.qpl" # Interface scale multiplier, e.g. 0.5 makes everything half-size. zoom: float = 1.0 diff --git a/src/images/foliage.jpg b/src/images/foliage.jpg new file mode 100644 index 00000000..2642d41e Binary files /dev/null and b/src/images/foliage.jpg differ diff --git a/src/themes/Foliage.qpl b/src/themes/Foliage.qpl new file mode 100644 index 00000000..baf16cf0 --- /dev/null +++ b/src/themes/Foliage.qpl @@ -0,0 +1,561 @@ +// vim: syntax=qml + +// Base variables + +real uiScale: window.settings.General.zoom + +int minimumSupportedWidth: 240 * uiScale +int minimumSupportedHeight: 120 * uiScale +int contentIsWideAbove: 472 * uiScale + +int baseElementsHeight: 36 * uiScale +int spacing: 12 * uiScale +int radius: 4 * uiScale +int animationDuration: 100 +real loadingElementsOpacity: 0.8 +real disabledElementsOpacity: 0.3 + + +fontSize: + int smaller: 13 * uiScale + int small: 13 * uiScale + int normal: 16 * uiScale + int big: 20 * uiScale + int bigger: 32 * uiScale + int biggest: 48 * uiScale + +fontFamily: + string sans: "Roboto" + string mono: "Hack" + // Fonts are comma-separated. + // For example, if you want colorful emoji, you could do: + // string sans: "Roboto,Joypixels" + // string mono: "Hack,Joypixels" + +colors: + int hue: 200 + + real intensity: 1.0 + real coloredTextIntensity: intensity * 71 + real dimColoredTextIntensity: intensity * 60 + + int saturation: 40 + int bgSaturation: saturation + int coloredTextSaturation: saturation + 20 + int dimColoredTextSaturation: saturation + + real opacity: 0.7 + + color weakBackground: + hsluv(hue, bgSaturation, intensity * 2.5, opacity) + color mediumBackground: + hsluv(hue, bgSaturation, intensity * 7, opacity) + color strongBackground: + hsluv(hue, bgSaturation * 2, intensity, opacity) + + color accentBackground: hsluv(hue, saturation, intensity * 40, 1) + color accentElement: hsluv(hue, saturation * 1.5, intensity * 52, 1) + color strongAccentElement: hsluv(hue, saturation * 1.5, intensity * 72, 1) + + color positiveBackground: + hsluv(155, saturation * 1.5, intensity * 65, 1) + + color middleBackground: + hsluv(60, saturation * 1.5, intensity * 65, 1) + + color negativeBackground: + hsluv(0, saturation * 1.5, intensity * 54, 1) + + color alertBackground: negativeBackground + + color brightText: hsluv(0, 0, intensity * 100) + color text: hsluv(0, 0, intensity * 85) + color halfDimText: hsluv(0, 0, intensity * 72) + color dimText: hsluv(0, 0, intensity * 60) + + color positiveText: hsluv(155, coloredTextSaturation, coloredTextIntensity) + color warningText: hsluv(60, coloredTextSaturation, coloredTextIntensity) + color errorText: hsluv(0, coloredTextSaturation, coloredTextIntensity) + color accentText: hsluv(hue, coloredTextSaturation, coloredTextIntensity) + + color link: hsluv(hue, coloredTextSaturation, coloredTextIntensity) + color code: hsluv(hue + 10, coloredTextSaturation, coloredTextIntensity) + + // Example of an animation, set running: true to enable + NumberAnimation on hue + running: false + from: 0 + to: 360 + duration: 10000 + loops: Animation.Infinite + +icons: + string preferredPack: "thin" + + // "transparent" to disable colorizing + color colorize: hsluv(0, 0, colors.intensity * 90) + color disabledColorize: "white" + + int smallDimension: 16 * uiScale + int dimension: 22 * uiScale + + +// Generic UI controls + +controls: + scrollBar: + int width: theme.spacing + + color track: colors.strongBackground + + color slider: colors.accentElement + color hoveredSlider: colors.accentElement + color pressedSlider: colors.strongAccentElement + + int sliderPadding: 2 + int sliderRadius: theme.radius + + box: + int defaultWidth: minimumSupportedWidth + color background: colors.mediumBackground + int radius: theme.radius + + popup: + int defaultWidth: minimumSupportedWidth * 1.75 + color background: colors.mediumBackground + color opaqueBackground: hsluv( + colors.hue, + colors.bgSaturation, + colors.intensity * 7, + 1 + ) + color windowOverlay: hsluv(0, 0, 0, 0.7) + + header: + color background: colors.strongBackground + + button: + color background: colors.strongBackground + color text: colors.text + color focusedBorder: colors.accentElement + int focusedBorderWidth: 2 + + color hoveredOverlay: hsluv(0, 0, 50, 0.2) + color pressedOverlay: hsluv(0, 0, 50, 0.5) + color checkedOverlay: colors.accentBackground + + tab: + color text: controls.button.text + color background: controls.button.background + color alternateBackground: hsluv( + colors.hue, + colors.bgSaturation * 1.25, + colors.intensity * 4, + Math.max(0.6, colors.opacity) + ) + + color bottomLine: background + color focusedBorder: colors.accentElement + int focusedBorderWidth: 1 + + color hoveredOverlay: controls.button.hoveredOverlay + color pressedOverlay: controls.button.pressedOverlay + color checkedOverlay: controls.button.checkedOverlay + + menu: + color background: hsluv( + colors.hue, + colors.bgSaturation * 2, + colors.intensity, + Math.max(0.9, colors.opacity), + ) + color border: "black" + real borderWidth: 2 + + menuItem: + color background: "transparent" + color text: controls.button.text + + color hoveredOverlay: controls.button.hoveredOverlay + color pressedOverlay: controls.button.hoveredOverlay + color checkedOverlay: controls.button.hoveredOverlay + + checkBox: + color checkIconColorize: colors.accentElement + color boxBackground: controls.button.background + int boxSize: 24 * uiScale + + color boxBorder: "black" + color boxHoveredBorder: colors.accentElement + color boxPressedBorder: colors.strongAccentElement + + color text: controls.button.text + color subtitle: colors.dimText + + listView: + color highlight: hsluv( + colors.hue, + colors.bgSaturation * 2, + colors.intensity * 1, + colors.opacity / 1.5, + ) + color highlightBorder: colors.strongAccentElement + int highlightBorderThickness: 1 + + textField: + color background: colors.strongBackground + color focusedBackground: background + + int borderWidth: 1 + color border: "transparent" + color focusedBorder: colors.accentElement + color errorBorder: colors.negativeBackground + + color text: colors.text + color focusedText: colors.text + color placeholderText: colors.dimText + + textArea: + color background: colors.strongBackground + + int borderWidth: 1 + color border: "transparent" + color focusedBorder: colors.accentElement + color errorBorder: colors.negativeBackground + + color text: colors.text + color placeholderText: controls.textField.placeholderText + + toolTip: + color background: colors.strongBackground + color text: colors.text + color border: "black" + int borderWidth: 2 + + progressBar: + int height: Math.max(2, spacing / 2) + color background: colors.strongBackground + color foreground: colors.accentElement + color pausedForeground: colors.middleBackground + color errorForeground: colors.negativeBackground + + circleProgressBar: + int thickness: Math.max(2, spacing / 2) + color background: colors.strongBackground + color foreground: colors.accentElement + color errorForeground: colors.negativeBackground + color text: colors.text + real indeterminateSpan: 0.5 // 0-1 + + slider: + int radius: 2 + int height: controls.progressBar.height + color background: controls.progressBar.background + color foreground: controls.progressBar.foreground + + handle: + int size: 20 + color inside: hsluv(0, 0, 90) + color pressedInside: "white" + color border: "black" + color pressedBorder: colors.strongAccentElement + + avatar: + int size: baseElementsHeight + int compactSize: baseElementsHeight / 2 + int radius: theme.radius + + hoveredImage: + int size: 192 + color background: hsluv(0, 0, 0, 0.4) + + background: + int saturation: colors.saturation + int lightness: Math.min(50, colors.intensity * 25) + real opacity: 1.0 + + letter: + int saturation: colors.saturation + 20 + int lightness: colors.intensity * 60 + real opacity: 1.0 + + displayName: + int saturation: colors.coloredTextSaturation + int lightness: colors.coloredTextIntensity + int dimSaturation: colors.dimColoredTextSaturation + int dimLightness: colors.dimColoredTextIntensity + + presence: + color online: colors.positiveBackground + color unavailable: colors.middleBackground + color offline: hsluv(0, 0, 60, 1) + color border: "black" + int borderWidth: 2 * uiScale + real opacity: 1.0 + real radius: 6.0 * uiScale + + +// Specific interface parts + +ui: + // The background image can be an URL or local file path + // (in the form file://, e.g. file:///home/user/images/foo.png). + // If not specified, the gradient will be shown instead. + url image: "../../images/foliage.jpg" + + point gradientStart: Qt.point(0, 0) + point gradientEnd: Qt.point(window.width, window.height) + + color gradientStartColor: + hsluv(colors.hue, 100, colors.intensity * 8) + color gradientEndColor: + hsluv(colors.hue + 50, 30, colors.intensity * 22) + + // To have a solid color instead, + // set gradientStartColor and gradientEndColor to the same value, e.g.: + // color gradientStartColor: hsluv(0, 0, 0, 0.5) + // color gradientEndColor: hsluv(0, 0, 0, 0.5) + + +mainPane: + color background: "transparent" + + topBar: + color background: colors.strongBackground + color nameVersionLabel: colors.text + + accountBar: + color background: colors.mediumBackground + + account: + color selectedBackground: colors.accentBackground + real selectedBackgroundOpacity: 0.3 + color selectedBorder: colors.strongAccentElement + int selectedBorderSize: 1 + + unreadIndicator: + color background: colors.strongBackground + color text: colors.accentText + bool bold: false + color border: Qt.darker(text, 2) + int borderWidth: 1 + int radius: theme.radius / 2 + + color highlightBackground: colors.strongBackground + color highlightText: colors.errorText + bool highlightBold: false + color highlightBorder: Qt.darker(highlightText, 2) + int highlightBorderWidth: 1 + int highlightRadius: theme.radius / 2 + + listView: + color background: colors.mediumBackground + real offlineOpacity: 0.5 + + account: + real collapsedOpacity: 0.3 + color background: "transparent" + color name: colors.text + + int avatarRadius: controls.avatar.radius + int collapsedAvatarRadius: controls.avatar.size / 2 + + room: + real leftRoomOpacity: 0.65 + + color background: "transparent" + color name: colors.text + color unreadName: colors.brightText + color lastEventDate: colors.halfDimText + + color subtitle: colors.dimText + color subtitleQuote: chat.message.quote + + int avatarRadius: controls.avatar.radius + int collapsedAvatarRadius: controls.avatar.radius + + unreadIndicator: + color background: colors.strongBackground + color text: colors.accentText + bool bold: false + color border: Qt.darker(text, 2) + int borderWidth: 1 + int radius: theme.radius / 2 + + color highlightBackground: colors.strongBackground + color highlightText: colors.errorText + bool highlightBold: false + color highlightBorder: Qt.darker(highlightText, 2) + int highlightBorderWidth: 1 + int highlightRadius: theme.radius / 2 + + bottomBar: + color background: "transparent" + color settingsButtonBackground: colors.strongBackground + color filterFieldBackground: colors.strongBackground + + +chat: + roomHeader: + color background: controls.header.background + color name: colors.text + color topic: colors.dimText + + roomPane: + color background: "transparent" + + topBar: + color background: colors.strongBackground + + listView: + color background: colors.mediumBackground + + member: + real invitedOpacity: 0.5 + + color background: "transparent" + color name: colors.text + color subtitle: colors.dimText + + color adminIcon: hsluv(60, colors.saturation * 2.25, 60) + color moderatorIcon: adminIcon + color invitedIcon: hsluv(0, colors.saturation * 2.25, 60) + + roomSettings: + color background: colors.mediumBackground + + bottomBar: + color background: colors.strongBackground + + inviteButton: + color background: "transparent" + + filterMembers: + color background: "transparent" + + eventList: + color background: "transparent" + + message: + int avatarSize: 56 * uiScale + int collapsedAvatarSize: 32 * uiScale + int avatarRadius: controls.avatar.radius + + int radius: theme.radius + int horizontalSpacing: theme.spacing / 1.25 + int verticalSpacing: theme.spacing / 1.75 + + color focusedHighlight: colors.accentBackground + real focusedHighlightOpacity: 0.4 + + color background: colors.weakBackground + color ownBackground: colors.mediumBackground + color checkedBackground: colors.accentBackground + + color body: colors.text + color date: colors.dimText + color localEcho: colors.dimText + color readCounter: colors.accentText + + color redactedBody: colors.dimText + + color noticeBody: colors.halfDimText + int noticeLineWidth: 1 * uiScale + + color quote: hsluv( + 135, colors.coloredTextSaturation, colors.coloredTextIntensity, + ) + color link: colors.link + color code: colors.code + + string styleSheet: + "* { white-space: pre-wrap }" + + "a { color: " + link + " }" + + "p { margin-top: 0 }" + + + "code { font-family: " + fontFamily.mono + "; " + + "color: " + code + " }" + + + "h1, h2, h3 { font-weight: normal }" + + "h1 { font-size: " + fontSize.biggest + "px }" + + "h2 { font-size: " + fontSize.bigger + "px }" + + "h3 { font-size: " + fontSize.big + "px }" + + "h4 { font-size: " + fontSize.normal + "px }" + + "h5 { font-size: " + fontSize.small + "px }" + + "h6 { font-size: " + fontSize.smaller + "px }" + + + "table { margin-top: " + theme.spacing + "px; " + + " margin-bottom: " + theme.spacing + "px }" + + + "td { padding-left: " + theme.spacing / 2 + "px; " + + " padding-right: " + theme.spacing / 2 + "px; " + + " padding-top: " + theme.spacing / 4 + "px; " + + " padding-bottom: " + theme.spacing / 4 + "px } " + + + "li { margin-top: " + theme.spacing / 2 + "px; " + + " margin-bottom: " + theme.spacing / 2 + "px; }" + + + ".sender { margin-bottom: " + spacing / 2 + " }" + + ".quote { color: " + quote + " }" + + + ".mention { text-decoration: none; }" + + ".room-id-mention, .room-alias-mention { font-weight: bold; }" + + string styleInclude: + '\n' + + real thumbnailCheckedOverlayOpacity: 0.4 + + daybreak: + color background: colors.mediumBackground + color text: colors.text + int radius: theme.radius + + inviteBanner: + color background: colors.mediumBackground + + leftBanner: + color background: colors.mediumBackground + + unknownDevices: + color background: colors.mediumBackground + + typingMembers: + color background: hsluv( + colors.hue, colors.saturation, colors.intensity * 9, 0.52 + ) + + replyBar: + color background: chat.typingMembers.background + + fileTransfer: + color background: chat.typingMembers.background + + userAutoCompletion: + color background: chat.typingMembers.background + int avatarsRadius: controls.avatar.radius + color displayNames: colors.text + color userIds: colors.dimText + + composer: + color background: colors.strongBackground + + uploadButton: + color background: "transparent" + + +mediaPlayer: + hoverPreview: + int maxHeight: 192 + + progress: + int height: 8 + color background: hsluv(0, 0, 0, 0.5) + + controls: + int iconSize: icons.dimension + int volumeSliderWidth: 100 + int speedSliderWidth: 100 + color background: hsluv( + colors.hue, colors.saturation * 1.25, colors.intensity * 2, 0.85, + )