diff --git a/README.md b/README.md index 98017a7e..7c51d26d 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ After this, verify the permissions of the installed plugin files. Dependencies on Pypi: pip3 install --user --upgrade \ - Pillow aiofiles dataclasses filetype lxml mistune uvloop + Pillow aiofiles dataclasses filetype hsluv lxml mistune uvloop Dependencies on Github (most recent version needed): diff --git a/TODO.md b/TODO.md index cb7c7f36..270fe9e8 100644 --- a/TODO.md +++ b/TODO.md @@ -2,6 +2,7 @@ - `QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling)` - Refactoring + - Use [Animators](https://doc.qt.io/qt-5/qml-qtquick-animator.html) - Sendbox - Use .mjs modules - SignIn/RememberAccount screens @@ -15,6 +16,7 @@ - When qml syntax highlighting supports string interpolation, use them - Fixes + - EditProfile avatar spacing in row mode - Message position after daybreak delegate - Keyboard flicking against top/bottom edge - Don't strip user spacing in html @@ -25,6 +27,9 @@ - [hr not working](https://bugreports.qt.io/browse/QTBUG-74342) - UI + - Messages editing + - Code highlighting + - Support GIF avatars - When reduced, show the full-window sidepane instead of Default page - Adapt shortcuts flicking speed to font size and DPI - Show error box if uploading avatar fails @@ -57,22 +62,27 @@ - Invite to room - Server selection - Register/Forgot? for SignIn dialog - - Scaling - - See [Text.fontSizeMode](https://doc.qt.io/qt-5/qml-qtquick-text.html#fontSizeMode-prop) - Add room - Leave room - Forget room warning popup - Prevent using the SendBox if no permission (power levels) - Spinner when loading past room events, images or clicking buttons - - Parse themes from JSON - - Distribute fonts + - Theming + - Thin white icons + - Copy the default theme to user data dir + - Distribute fonts + - preferredIconPack: accept multiple values + - Find icon packs in user data dir + - Correctly implement uiScale/fontScale + ctrl+-= keys + - See [Text.fontSizeMode](https://doc.qt.io/qt-5/qml-qtquick-text.html#fontSizeMode-prop) + - Selecting background image + - Way to round avatar corners to allow box radius + - If avatar is set, name color from average color? + - Accent color from background - Settings page - Message/text selection - Custom file picker for Linux... - - Way to round avatar corners to allow box radius - - If avatar is set, name color from average color? - - Accent color from background - Major features - E2E diff --git a/src/icons/add-account.svg b/src/icons/dark-filled/add-account.svg similarity index 100% rename from src/icons/add-account.svg rename to src/icons/dark-filled/add-account.svg diff --git a/src/icons/apply.svg b/src/icons/dark-filled/apply.svg similarity index 100% rename from src/icons/apply.svg rename to src/icons/dark-filled/apply.svg diff --git a/src/icons/cancel.svg b/src/icons/dark-filled/cancel.svg similarity index 100% rename from src/icons/cancel.svg rename to src/icons/dark-filled/cancel.svg diff --git a/src/icons/email.svg b/src/icons/dark-filled/email.svg similarity index 100% rename from src/icons/email.svg rename to src/icons/dark-filled/email.svg diff --git a/src/icons/expand.svg b/src/icons/dark-filled/expand.svg similarity index 100% rename from src/icons/expand.svg rename to src/icons/dark-filled/expand.svg diff --git a/src/icons/forget-room.svg b/src/icons/dark-filled/forget-room.svg similarity index 100% rename from src/icons/forget-room.svg rename to src/icons/dark-filled/forget-room.svg diff --git a/src/icons/hourglass.svg b/src/icons/dark-filled/hourglass.svg similarity index 100% rename from src/icons/hourglass.svg rename to src/icons/dark-filled/hourglass.svg diff --git a/src/icons/invite-accept.svg b/src/icons/dark-filled/invite-accept.svg similarity index 100% rename from src/icons/invite-accept.svg rename to src/icons/dark-filled/invite-accept.svg diff --git a/src/icons/invite-decline.svg b/src/icons/dark-filled/invite-decline.svg similarity index 100% rename from src/icons/invite-decline.svg rename to src/icons/dark-filled/invite-decline.svg diff --git a/src/icons/join.svg b/src/icons/dark-filled/join.svg similarity index 100% rename from src/icons/join.svg rename to src/icons/dark-filled/join.svg diff --git a/src/icons/phone.svg b/src/icons/dark-filled/phone.svg similarity index 100% rename from src/icons/phone.svg rename to src/icons/dark-filled/phone.svg diff --git a/src/icons/placeholder.svg b/src/icons/dark-filled/placeholder.svg similarity index 100% rename from src/icons/placeholder.svg rename to src/icons/dark-filled/placeholder.svg diff --git a/src/icons/reduced-menu.svg b/src/icons/dark-filled/reduced-menu.svg similarity index 100% rename from src/icons/reduced-menu.svg rename to src/icons/dark-filled/reduced-menu.svg diff --git a/src/icons/reduced-room-buttons.svg b/src/icons/dark-filled/reduced-room-buttons.svg similarity index 100% rename from src/icons/reduced-room-buttons.svg rename to src/icons/dark-filled/reduced-room-buttons.svg diff --git a/src/icons/room-view-files.svg b/src/icons/dark-filled/room-view-files.svg similarity index 100% rename from src/icons/room-view-files.svg rename to src/icons/dark-filled/room-view-files.svg diff --git a/src/icons/room-view-history.svg b/src/icons/dark-filled/room-view-history.svg similarity index 100% rename from src/icons/room-view-history.svg rename to src/icons/dark-filled/room-view-history.svg diff --git a/src/icons/room-view-members.svg b/src/icons/dark-filled/room-view-members.svg similarity index 100% rename from src/icons/room-view-members.svg rename to src/icons/dark-filled/room-view-members.svg diff --git a/src/icons/room-view-notifications.svg b/src/icons/dark-filled/room-view-notifications.svg similarity index 100% rename from src/icons/room-view-notifications.svg rename to src/icons/dark-filled/room-view-notifications.svg diff --git a/src/icons/room-view-settings.svg b/src/icons/dark-filled/room-view-settings.svg similarity index 100% rename from src/icons/room-view-settings.svg rename to src/icons/dark-filled/room-view-settings.svg diff --git a/src/icons/search.svg b/src/icons/dark-filled/search.svg similarity index 100% rename from src/icons/search.svg rename to src/icons/dark-filled/search.svg diff --git a/src/icons/set-status.svg b/src/icons/dark-filled/set-status.svg similarity index 100% rename from src/icons/set-status.svg rename to src/icons/dark-filled/set-status.svg diff --git a/src/icons/settings.svg b/src/icons/dark-filled/settings.svg similarity index 100% rename from src/icons/settings.svg rename to src/icons/dark-filled/settings.svg diff --git a/src/icons/status.svg b/src/icons/dark-filled/status.svg similarity index 100% rename from src/icons/status.svg rename to src/icons/dark-filled/status.svg diff --git a/src/icons/typing.svg b/src/icons/dark-filled/typing.svg similarity index 100% rename from src/icons/typing.svg rename to src/icons/dark-filled/typing.svg diff --git a/src/icons/unknown-devices-inspect.svg b/src/icons/dark-filled/unknown-devices-inspect.svg similarity index 100% rename from src/icons/unknown-devices-inspect.svg rename to src/icons/dark-filled/unknown-devices-inspect.svg diff --git a/src/icons/unknown-devices-warning.svg b/src/icons/dark-filled/unknown-devices-warning.svg similarity index 100% rename from src/icons/unknown-devices-warning.svg rename to src/icons/dark-filled/unknown-devices-warning.svg diff --git a/src/icons/upload-avatar.svg b/src/icons/dark-filled/upload-avatar.svg similarity index 100% rename from src/icons/upload-avatar.svg rename to src/icons/dark-filled/upload-avatar.svg diff --git a/src/icons/username.svg b/src/icons/dark-filled/username.svg similarity index 100% rename from src/icons/username.svg rename to src/icons/dark-filled/username.svg diff --git a/src/icons/light-thin/add-account.svg b/src/icons/light-thin/add-account.svg new file mode 100644 index 00000000..bcea32ec --- /dev/null +++ b/src/icons/light-thin/add-account.svg @@ -0,0 +1,54 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/src/icons/light-thin/apply.svg b/src/icons/light-thin/apply.svg new file mode 100644 index 00000000..c2bef64e --- /dev/null +++ b/src/icons/light-thin/apply.svg @@ -0,0 +1,51 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/src/icons/light-thin/cancel.svg b/src/icons/light-thin/cancel.svg new file mode 100644 index 00000000..584a4121 --- /dev/null +++ b/src/icons/light-thin/cancel.svg @@ -0,0 +1,51 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/src/icons/light-thin/email.svg b/src/icons/light-thin/email.svg new file mode 100644 index 00000000..5b0b654b --- /dev/null +++ b/src/icons/light-thin/email.svg @@ -0,0 +1,13 @@ + + + + + + + diff --git a/src/icons/light-thin/expand.svg b/src/icons/light-thin/expand.svg new file mode 100644 index 00000000..8e0400c9 --- /dev/null +++ b/src/icons/light-thin/expand.svg @@ -0,0 +1,13 @@ + + + + + + + diff --git a/src/icons/light-thin/forget-room.svg b/src/icons/light-thin/forget-room.svg new file mode 100644 index 00000000..0a837a00 --- /dev/null +++ b/src/icons/light-thin/forget-room.svg @@ -0,0 +1,51 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/src/icons/light-thin/hourglass.svg b/src/icons/light-thin/hourglass.svg new file mode 100644 index 00000000..ec6e3ed0 --- /dev/null +++ b/src/icons/light-thin/hourglass.svg @@ -0,0 +1,32 @@ + + + Svg Vector Icons : http://www.onlinewebfonts.com/icon image/svg+xml + + \ No newline at end of file diff --git a/src/icons/light-thin/invite-accept.svg b/src/icons/light-thin/invite-accept.svg new file mode 100644 index 00000000..c2bef64e --- /dev/null +++ b/src/icons/light-thin/invite-accept.svg @@ -0,0 +1,51 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/src/icons/light-thin/invite-decline.svg b/src/icons/light-thin/invite-decline.svg new file mode 100644 index 00000000..584a4121 --- /dev/null +++ b/src/icons/light-thin/invite-decline.svg @@ -0,0 +1,51 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/src/icons/light-thin/join.svg b/src/icons/light-thin/join.svg new file mode 100644 index 00000000..64c46025 --- /dev/null +++ b/src/icons/light-thin/join.svg @@ -0,0 +1,13 @@ + + + + + + + diff --git a/src/icons/light-thin/phone.svg b/src/icons/light-thin/phone.svg new file mode 100644 index 00000000..52eca0a3 --- /dev/null +++ b/src/icons/light-thin/phone.svg @@ -0,0 +1,13 @@ + + + + + + + diff --git a/src/icons/light-thin/placeholder.svg b/src/icons/light-thin/placeholder.svg new file mode 100644 index 00000000..c83ab1d4 --- /dev/null +++ b/src/icons/light-thin/placeholder.svg @@ -0,0 +1,13 @@ + + + + + + + diff --git a/src/icons/light-thin/reduced-menu.svg b/src/icons/light-thin/reduced-menu.svg new file mode 100644 index 00000000..ab64a32b --- /dev/null +++ b/src/icons/light-thin/reduced-menu.svg @@ -0,0 +1,13 @@ + + + + + + + diff --git a/src/icons/light-thin/reduced-room-buttons.svg b/src/icons/light-thin/reduced-room-buttons.svg new file mode 100644 index 00000000..fe89d2df --- /dev/null +++ b/src/icons/light-thin/reduced-room-buttons.svg @@ -0,0 +1,13 @@ + + + + + + + diff --git a/src/icons/light-thin/room-view-files.svg b/src/icons/light-thin/room-view-files.svg new file mode 100644 index 00000000..03dc63aa --- /dev/null +++ b/src/icons/light-thin/room-view-files.svg @@ -0,0 +1,13 @@ + + + + + + + diff --git a/src/icons/light-thin/room-view-history.svg b/src/icons/light-thin/room-view-history.svg new file mode 100644 index 00000000..bc1b5411 --- /dev/null +++ b/src/icons/light-thin/room-view-history.svg @@ -0,0 +1,13 @@ + + + + + + + diff --git a/src/icons/light-thin/room-view-members.svg b/src/icons/light-thin/room-view-members.svg new file mode 100644 index 00000000..c5c5eab8 --- /dev/null +++ b/src/icons/light-thin/room-view-members.svg @@ -0,0 +1,13 @@ + + + + + + + diff --git a/src/icons/light-thin/room-view-notifications.svg b/src/icons/light-thin/room-view-notifications.svg new file mode 100644 index 00000000..306cb5bc --- /dev/null +++ b/src/icons/light-thin/room-view-notifications.svg @@ -0,0 +1,13 @@ + + + + + + + diff --git a/src/icons/light-thin/room-view-settings.svg b/src/icons/light-thin/room-view-settings.svg new file mode 100644 index 00000000..c5b744f1 --- /dev/null +++ b/src/icons/light-thin/room-view-settings.svg @@ -0,0 +1,13 @@ + + + + + + + diff --git a/src/icons/light-thin/search.svg b/src/icons/light-thin/search.svg new file mode 100644 index 00000000..9a0c00a8 --- /dev/null +++ b/src/icons/light-thin/search.svg @@ -0,0 +1,13 @@ + + + + + + + diff --git a/src/icons/light-thin/set-status.svg b/src/icons/light-thin/set-status.svg new file mode 100644 index 00000000..99a9fc53 --- /dev/null +++ b/src/icons/light-thin/set-status.svg @@ -0,0 +1,13 @@ + + + + + + + diff --git a/src/icons/light-thin/settings.svg b/src/icons/light-thin/settings.svg new file mode 100644 index 00000000..b6b6910d --- /dev/null +++ b/src/icons/light-thin/settings.svg @@ -0,0 +1,13 @@ + + + + + + + diff --git a/src/icons/light-thin/status.svg b/src/icons/light-thin/status.svg new file mode 100644 index 00000000..595c8530 --- /dev/null +++ b/src/icons/light-thin/status.svg @@ -0,0 +1,13 @@ + + + + + + + diff --git a/src/icons/light-thin/typing.svg b/src/icons/light-thin/typing.svg new file mode 100644 index 00000000..99a9fc53 --- /dev/null +++ b/src/icons/light-thin/typing.svg @@ -0,0 +1,13 @@ + + + + + + + diff --git a/src/icons/light-thin/unknown-devices-inspect.svg b/src/icons/light-thin/unknown-devices-inspect.svg new file mode 100644 index 00000000..4d104b7a --- /dev/null +++ b/src/icons/light-thin/unknown-devices-inspect.svg @@ -0,0 +1,51 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/src/icons/light-thin/unknown-devices-warning.svg b/src/icons/light-thin/unknown-devices-warning.svg new file mode 100644 index 00000000..6db60845 --- /dev/null +++ b/src/icons/light-thin/unknown-devices-warning.svg @@ -0,0 +1,13 @@ + + + + + + + diff --git a/src/icons/light-thin/upload-avatar.svg b/src/icons/light-thin/upload-avatar.svg new file mode 100644 index 00000000..d270d3f6 --- /dev/null +++ b/src/icons/light-thin/upload-avatar.svg @@ -0,0 +1 @@ + diff --git a/src/icons/light-thin/username.svg b/src/icons/light-thin/username.svg new file mode 100644 index 00000000..c5c5eab8 --- /dev/null +++ b/src/icons/light-thin/username.svg @@ -0,0 +1,13 @@ + + + + + + + diff --git a/src/icons/none.svg b/src/icons/none.svg deleted file mode 100644 index 2d320c47..00000000 --- a/src/icons/none.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/python/backend.py b/src/python/backend.py index db4cd675..1c0f37b7 100644 --- a/src/python/backend.py +++ b/src/python/backend.py @@ -3,7 +3,9 @@ import asyncio import random -from typing import Dict, Optional, Set, Tuple +from typing import Dict, List, Optional, Set, Tuple + +import hsluv from .app import App from .events import users @@ -127,3 +129,9 @@ class Backend: @staticmethod def inlinify(html: str) -> str: return HTML_FILTER.filter_inline(html) + + + @staticmethod + def hsluv(hue: int, saturation: int, lightness: int) -> List[float]: + # (0-360, 0-100, 0-100) -> [0-1, 0-1, 0-1] + return hsluv.hsluv_to_rgb([hue, saturation, lightness]) diff --git a/src/python/theme_parser.py b/src/python/theme_parser.py index 4f80d8ca..731f43e2 100644 --- a/src/python/theme_parser.py +++ b/src/python/theme_parser.py @@ -9,14 +9,14 @@ PROPERTY_TYPES = {"bool", "double", "int", "list", "real", "string", "url", def _add_property(line: str) -> str: - if re.match(r"^\s*[a-zA-Z0-9_]+\s*:$", line): + if re.match(r"^\s*[a-zA-Z\d_]+\s*:$", line): return re.sub(r"^(\s*)(\S*\s*):$", r"\1readonly property QtObject \2: QtObject", line) types = "|".join(PROPERTY_TYPES) if re.match(fr"^\s*({types}) [a-zA-Z\d_]+\s*:", line): - return re.sub(r"^(\s*)(\S*)", r"\1readonly property \2", line) + return re.sub(r"^(\s*)(\S*)", r"\1property \2", line) return line @@ -60,8 +60,12 @@ def _process_lines(content: str) -> Generator[str, None, None]: def convert_to_qml(theme_content: str) -> str: lines = [ "import QtQuick 2.12", - 'import "utils.js" as Ut', + 'import "Base"', + 'import "utils.js" as Utils', "QtObject {", + " function hsluv(h, s, l, a) { return Utils.hsluv(h, s, l, a) }", + " function hsl(h, s, l) { return Utils.hsl(h, s, l) }", + " function hsla(h, s, l, a) { return Utils.hsla(h, s, l, a) }", " id: theme", ] lines += [f" {line}" for line in _process_lines(theme_content)] diff --git a/src/qml/Base/HAvatar.qml b/src/qml/Base/HAvatar.qml index 160c364b..a2b8ad92 100644 --- a/src/qml/Base/HAvatar.qml +++ b/src/qml/Base/HAvatar.qml @@ -8,8 +8,8 @@ import "../utils.js" as Utils HRectangle { id: avatar - implicitWidth: theme.avatar.size - implicitHeight: theme.avatar.size + implicitWidth: theme.controls.avatar.size + implicitHeight: theme.controls.avatar.size property string name: "" property var imageUrl: "" @@ -20,18 +20,27 @@ HRectangle { readonly property var params: Utils.thumbnailParametersFor(width, height) - color: imageUrl ? "transparent" : - name ? Utils.avatarColor(name) : - theme.avatar.background.unknown + color: avatarImage.visible ? "transparent" : Utils.hsla( + name ? Utils.hueFrom(name) : 0, + name ? theme.controls.avatar.background.saturation : 0, + theme.controls.avatar.background.lightness, + theme.controls.avatar.background.opacity + ) HLabel { z: 1 anchors.centerIn: parent - visible: ! imageUrl + visible: ! avatarImage.visible text: name ? name.charAt(0) : "?" - color: theme.avatar.letter font.pixelSize: parent.height / 1.4 + + color: Utils.hsla( + name ? Utils.hueFrom(name) : 0, + name ? theme.controls.avatar.letter.saturation : 0, + theme.controls.avatar.letter.lightness, + theme.controls.avatar.letter.opacity + ) } HImage { diff --git a/src/qml/Base/HBusyIndicator.qml b/src/qml/Base/HBusyIndicator.qml index 8599b73d..5f8a584c 100644 --- a/src/qml/Base/HBusyIndicator.qml +++ b/src/qml/Base/HBusyIndicator.qml @@ -1,4 +1,24 @@ import QtQuick 2.12 import QtQuick.Controls 2.12 -BusyIndicator {} +BusyIndicator { + id: indicator + implicitWidth: Math.min(192, Math.max(64, parent.width / 5)) + implicitHeight: 10 + + contentItem: Item { + Rectangle { + id: rect + width: indicator.height + height: indicator.height + radius: height / 2 + + XAnimator on x { + from: 0 + to: indicator.width - rect.width + duration: 500 + onStopped: {[from, to] = [to, from]; start()} + } + } + } +} diff --git a/src/qml/Base/HIcon.qml b/src/qml/Base/HIcon.qml index e0385fec..c7f0d18e 100644 --- a/src/qml/Base/HIcon.qml +++ b/src/qml/Base/HIcon.qml @@ -4,10 +4,14 @@ import QtQuick 2.12 HImage { - property var svgName: null + property string svgName: "" property int dimension: 20 - source: "../../icons/" + (svgName || "none") + ".svg" + source: + svgName ? + ("../../icons/" + theme.preferredIconPack + "/" + svgName + ".svg") : + "" + sourceSize.width: svgName ? dimension : 0 sourceSize.height: svgName ? dimension : 0 } diff --git a/src/qml/Base/HInteractiveRectangle.qml b/src/qml/Base/HInteractiveRectangle.qml index 43d44136..ed05181e 100644 --- a/src/qml/Base/HInteractiveRectangle.qml +++ b/src/qml/Base/HInteractiveRectangle.qml @@ -8,18 +8,20 @@ HRectangle { property bool checked: false readonly property QtObject _ir: theme.controls.interactiveRectangle + color: _ir.background - property color normalColor: _ir.background - property color hoveredColor: _ir.hoveredBackground - property color pressedColor: _ir.pressedBackground - property color checkedColor: _ir.checkedBackground + HRectangle { + anchors.fill: parent + visible: opacity > 0 - color: checked ? checkedColor : - // tap.pressed ? pressedColor : - hover.hovered ? hoveredColor : - normalColor + color: checked ? _ir.checkedOverlay : _ir.hoveredOverlay - Behavior on color { HColorAnimation { factor: 0.66 } } + opacity: checked ? _ir.checkedOpacity : + hover.hovered ? _ir.hoveredOpacity : + 0 + + Behavior on opacity { HNumberAnimation { factor: 0.66 } } + } HoverHandler { id: hover } TapHandler { id: tap } diff --git a/src/qml/Base/HInterfaceBox.qml b/src/qml/Base/HInterfaceBox.qml index 9668752b..dd59b06a 100644 --- a/src/qml/Base/HInterfaceBox.qml +++ b/src/qml/Base/HInterfaceBox.qml @@ -56,7 +56,7 @@ HScalingBox { onClicked: buttonCallbacks[modelData.name](button) Layout.fillWidth: true - Layout.preferredHeight: theme.avatar.size + Layout.preferredHeight: theme.controls.avatar.size } } } diff --git a/src/qml/Base/HLabel.qml b/src/qml/Base/HLabel.qml index 8e072222..e31f6c5d 100644 --- a/src/qml/Base/HLabel.qml +++ b/src/qml/Base/HLabel.qml @@ -2,16 +2,15 @@ // This file is part of harmonyqml, licensed under LGPLv3. import QtQuick.Controls 2.12 +import QtQuick 2.12 Label { font.family: theme.fontFamily.sans font.pixelSize: theme.fontSize.normal textFormat: Label.PlainText - color: theme.colors.foreground - style: Label.Outline - styleColor: theme.colors.textBorder - linkColor: theme.colors.accentDarker + color: theme.colors.text + linkColor: theme.colors.link maximumLineCount: elide == Label.ElideNone ? Number.MAX_VALUE : 1 } diff --git a/src/qml/Base/HNoticePage.qml b/src/qml/Base/HNoticePage.qml index b9caa485..1cfb5449 100644 --- a/src/qml/Base/HNoticePage.qml +++ b/src/qml/Base/HNoticePage.qml @@ -30,8 +30,8 @@ HRowLayout { background: Rectangle { id: noticeLabelBackground - color: theme.box.background - radius: theme.box.radius + color: theme.controls.box.background + radius: theme.controls.box.radius } } } diff --git a/src/qml/Base/HPage.qml b/src/qml/Base/HPage.qml index e1c610f4..484c2463 100644 --- a/src/qml/Base/HPage.qml +++ b/src/qml/Base/HPage.qml @@ -38,7 +38,7 @@ SwipeView { header: HRectangle { implicitWidth: parent ? parent.width : 0 - color: theme.pageHeadersBackground + color: theme.controls.header.background height: ! hideHeaderUnderHeight || window.height >= diff --git a/src/qml/Base/HRectangle.qml b/src/qml/Base/HRectangle.qml index 33a4b7ea..b7b3f20d 100644 --- a/src/qml/Base/HRectangle.qml +++ b/src/qml/Base/HRectangle.qml @@ -5,5 +5,5 @@ import QtQuick 2.12 Rectangle { id: rectangle - color: theme.sidePane.background + color: theme.controls.box.background } diff --git a/src/qml/Base/HScalingBox.qml b/src/qml/Base/HScalingBox.qml index da188c51..181a9d09 100644 --- a/src/qml/Base/HScalingBox.qml +++ b/src/qml/Base/HScalingBox.qml @@ -11,7 +11,7 @@ HRectangle { readonly property int baseWidth: baseHeight * widthForHeight readonly property int margins: baseHeight * 0.03 - color: theme.box.background + color: theme.controls.box.background height: Math.min(parent.height, baseHeight) width: Math.min(parent.width, baseWidth) scale: Math.max(1, parent.height / startScalingUpAboveHeight) diff --git a/src/qml/Base/HScrollableTextArea.qml b/src/qml/Base/HScrollableTextArea.qml index 8234c602..9ef5cf5b 100644 --- a/src/qml/Base/HScrollableTextArea.qml +++ b/src/qml/Base/HScrollableTextArea.qml @@ -24,7 +24,7 @@ ScrollView { font.family: theme.fontFamily.sans font.pixelSize: theme.fontSize.normal - color: theme.colors.foreground + color: theme.controls.textArea.text background: Rectangle { id: textAreaBackground color: theme.controls.textArea.background diff --git a/src/qml/Base/HTextField.qml b/src/qml/Base/HTextField.qml index 18ddeb57..31da426b 100644 --- a/src/qml/Base/HTextField.qml +++ b/src/qml/Base/HTextField.qml @@ -18,16 +18,17 @@ TextField { property color focusedBorderColor: _tf.focusedBorder property alias radius: textFieldBackground.radius - color: theme.colors.foreground + color: activeFocus ? _tf.focusedText : _tf.text + background: Rectangle { id: textFieldBackground color: field.activeFocus ? focusedBackgroundColor : backgroundColor border.color: field.activeFocus ? focusedBorderColor : borderColor border.width: bordered ? theme.controls.textField.borderWidth : 0 - Behavior on color { HColorAnimation {} } - Behavior on border.color { HColorAnimation {} } - Behavior on border.width { HNumberAnimation {} } + Behavior on color { HColorAnimation { factor: 0.5 } } + Behavior on border.color { HColorAnimation { factor: 0.5 } } + Behavior on border.width { HNumberAnimation { factor: 0.5 } } } selectByMouse: true diff --git a/src/qml/Base/HUIButton.qml b/src/qml/Base/HUIButton.qml index fe2bf63b..5e8d856e 100644 --- a/src/qml/Base/HUIButton.qml +++ b/src/qml/Base/HUIButton.qml @@ -51,7 +51,8 @@ HBaseButton { horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter color: enabled ? - theme.colors.foreground : theme.colors.foregroundDim2 + theme.controls.button.text : + theme.controls.button.disabledText Behavior on color { HNumberAnimation {} } Layout.fillWidth: true diff --git a/src/qml/Chat/ChatSplitView.qml b/src/qml/Chat/ChatSplitView.qml index abf63f7e..58620f11 100644 --- a/src/qml/Chat/ChatSplitView.qml +++ b/src/qml/Chat/ChatSplitView.qml @@ -16,6 +16,9 @@ HSplitView { Layout.fillWidth: true EventList { + // Avoid a certain binding loop + Layout.minimumWidth: theme.minimumSupportedWidth + Layout.fillWidth: true Layout.fillHeight: true } @@ -79,11 +82,11 @@ HSplitView { to: target.oldWidth onStopped: target.Layout.minimumWidth = Qt.binding( - () => theme.avatar.size + () => theme.controls.avatar.size ) } - collapsed: width < theme.avatar.size + theme.spacing + collapsed: width < theme.controls.avatar.size + theme.spacing property bool wasSnapped: false property int referenceWidth: roomHeader.buttonsWidth @@ -110,7 +113,7 @@ HSplitView { } width: referenceWidth // Initial width - Layout.minimumWidth: theme.avatar.size + Layout.minimumWidth: theme.controls.avatar.size Layout.maximumWidth: parent.width - theme.minimumSupportedWidthPlusSpacing } diff --git a/src/qml/Chat/RoomHeader.qml b/src/qml/Chat/RoomHeader.qml index cc4d9b21..6fe2d772 100644 --- a/src/qml/Chat/RoomHeader.qml +++ b/src/qml/Chat/RoomHeader.qml @@ -38,6 +38,7 @@ HRectangle { id: roomName text: displayName font.pixelSize: theme.fontSize.big + color: theme.chat.roomHeader.name elide: Text.ElideRight verticalAlignment: Text.AlignVCenter @@ -54,6 +55,7 @@ HRectangle { id: roomTopic text: topic font.pixelSize: theme.fontSize.small + color: theme.chat.roomHeader.topic elide: Text.ElideRight verticalAlignment: Text.AlignVCenter @@ -78,6 +80,7 @@ HRectangle { "members", "files", "notifications", "history", "settings" ] HUIButton { + backgroundColor: theme.chat.selectViewBar.background iconName: "room-view-" + modelData iconDimension: 22 autoExclusive: true @@ -96,9 +99,13 @@ HRectangle { HUIButton { id: expandButton z: 1 + width: theme.controls.avatar.size + height: width anchors.right: parent.right opacity: collapseButtons ? 1 : 0 visible: opacity > 0 + + backgroundColor: theme.chat.selectViewBar.background iconName: "reduced-room-buttons" Behavior on opacity { diff --git a/src/qml/Chat/RoomSidePane/RoomSidePane.qml b/src/qml/Chat/RoomSidePane/RoomSidePane.qml index 2fccd993..13252bc4 100644 --- a/src/qml/Chat/RoomSidePane/RoomSidePane.qml +++ b/src/qml/Chat/RoomSidePane/RoomSidePane.qml @@ -7,6 +7,7 @@ import "../../Base" HRectangle { id: roomSidePane + color: theme.chat.roomSidePane.background property bool collapsed: false property var activeView: null diff --git a/src/qml/Chat/Timeline/Daybreak.qml b/src/qml/Chat/Timeline/Daybreak.qml index 0b3c0acf..41c24f5b 100644 --- a/src/qml/Chat/Timeline/Daybreak.qml +++ b/src/qml/Chat/Timeline/Daybreak.qml @@ -6,7 +6,7 @@ import "../../Base" HNoticePage { text: model.date.toLocaleDateString() - color: theme.chat.daybreak.foreground + color: theme.chat.daybreak.text backgroundColor: theme.chat.daybreak.background radius: theme.chat.daybreak.radius } diff --git a/src/qml/LoadingScreen.qml b/src/qml/LoadingScreen.qml index dc0c34dc..95d3aed3 100644 --- a/src/qml/LoadingScreen.qml +++ b/src/qml/LoadingScreen.qml @@ -3,9 +3,11 @@ import QtQuick 2.12 import "Base" +import "utils.js" as Utils -Rectangle { - color: "lightgray" +HRectangle { + color: theme ? theme.controls.box.background : "#0f1222" + Behavior on color { HNumberAnimation {} } HBusyIndicator { anchors.centerIn: parent diff --git a/src/qml/Pages/EditAccount/EditAccount.qml b/src/qml/Pages/EditAccount/EditAccount.qml index 75cc9077..df6ceedc 100644 --- a/src/qml/Pages/EditAccount/EditAccount.qml +++ b/src/qml/Pages/EditAccount/EditAccount.qml @@ -24,7 +24,7 @@ HPage { ) HRectangle { - color: ready ? theme.box.background : "transparent" + color: ready ? theme.controls.box.background : "transparent" Behavior on color { HColorAnimation {} } Layout.alignment: Qt.AlignCenter diff --git a/src/qml/Shortcuts.qml b/src/qml/Shortcuts.qml index 9dfe19cf..d8d0cd05 100644 --- a/src/qml/Shortcuts.qml +++ b/src/qml/Shortcuts.qml @@ -31,4 +31,21 @@ Item { sequence: "Alt+Shift+D" onActivated: if (window.debug) { py.call("APP.pdb") } } + + /* + Shortcut { + sequence: "Ctrl+-" + onActivated: theme.fontScale = Math.max(0.1, theme.fontScale - 0.1) + } + + Shortcut { + sequence: "Ctrl++" + onActivated: theme.fontScale = Math.min(10, theme.fontScale + 0.1) + } + + Shortcut { + sequence: "Ctrl+=" + onActivated: theme.fontScale = 1.0 + } + */ } diff --git a/src/qml/SidePane/AccountDelegate.qml b/src/qml/SidePane/AccountDelegate.qml index fd9b3fe3..05f5dde4 100644 --- a/src/qml/SidePane/AccountDelegate.qml +++ b/src/qml/SidePane/AccountDelegate.qml @@ -8,6 +8,7 @@ import "../Base" Column { id: accountDelegate width: parent.width + spacing: theme.spacing / 2 property var userInfo: users.find(model.userId) property bool expanded: true @@ -23,8 +24,7 @@ Column { HInteractiveRectangle { width: parent.width height: childrenRect.height - - normalColor: theme.sidePane.account.background + color: theme.sidePane.account.background TapHandler { onTapped: pageStack.showPage( @@ -48,6 +48,7 @@ Column { HLabel { id: accountLabel + color: theme.sidePane.account.name text: userInfo.displayName || model.userId font.pixelSize: theme.fontSize.big elide: HLabel.ElideRight diff --git a/src/qml/SidePane/RoomCategoryDelegate.qml b/src/qml/SidePane/RoomCategoryDelegate.qml index b3360eb4..6fb0ba34 100644 --- a/src/qml/SidePane/RoomCategoryDelegate.qml +++ b/src/qml/SidePane/RoomCategoryDelegate.qml @@ -42,6 +42,8 @@ Column { text: model.name font.weight: Font.DemiBold elide: Text.ElideRight + topPadding: theme.spacing / 2 + bottomPadding: topPadding Layout.leftMargin: sidePane.currentSpacing Layout.fillWidth: true diff --git a/src/qml/SidePane/RoomDelegate.qml b/src/qml/SidePane/RoomDelegate.qml index c5f9d63f..04bdaef9 100644 --- a/src/qml/SidePane/RoomDelegate.qml +++ b/src/qml/SidePane/RoomDelegate.qml @@ -10,6 +10,7 @@ HInteractiveRectangle { id: roomDelegate width: roomList.width height: childrenRect.height + color: theme.sidePane.room.background TapHandler { onTapped: pageStack.showRoom( @@ -38,6 +39,7 @@ HInteractiveRectangle { HLabel { id: roomLabel + color: theme.sidePane.room.name text: model.displayName || "Empty room" textFormat: model.displayName? Text.PlainText : Text.StyledText @@ -68,6 +70,7 @@ HInteractiveRectangle { onLastEvChanged: text = getText(lastEv) id: subtitleLabel + color: theme.sidePane.room.subtitle visible: Boolean(text) textFormat: Text.StyledText diff --git a/src/qml/SidePane/SidePane.qml b/src/qml/SidePane/SidePane.qml index 6a4d2b8e..62248df0 100644 --- a/src/qml/SidePane/SidePane.qml +++ b/src/qml/SidePane/SidePane.qml @@ -11,6 +11,8 @@ HRectangle { opacity: mainUI.accountsPresent && ! reduce ? 1 : 0 visible: opacity > 0 + color: theme.sidePane.background + property real autoWidthRatio: theme.sidePane.autoWidthRatio property bool manuallyResizing: false property bool manuallyResized: false diff --git a/src/qml/UI.qml b/src/qml/UI.qml index 0202303f..8be52e74 100644 --- a/src/qml/UI.qml +++ b/src/qml/UI.qml @@ -8,8 +8,9 @@ import QtQuick.Window 2.7 import "Base" import "SidePane" -Item { +HRectangle { id: mainUI + color: theme.ui.background Connections { target: py @@ -34,6 +35,7 @@ Item { accounts.count > 0 || py.loadingAccounts HImage { + visible: false id: mainUIBackground fillMode: Image.PreserveAspectCrop source: "../images/background.jpg" diff --git a/src/qml/Window.qml b/src/qml/Window.qml index 8d8b6d68..02878718 100644 --- a/src/qml/Window.qml +++ b/src/qml/Window.qml @@ -8,13 +8,14 @@ import "Models" ApplicationWindow { id: window + flags: Qt.WA_TranslucentBackground minimumWidth: theme ? theme.minimumSupportedWidth : 240 minimumHeight: theme ? theme.minimumSupportedHeight : 120 width: 640 height: 480 visible: true title: "Harmony QML" - color: "black" + color: "transparent" Component.onCompleted: { Qt.application.organization = "harmonyqml" diff --git a/src/qml/utils.js b/src/qml/utils.js index 6f185d00..f521b7d1 100644 --- a/src/qml/utils.js +++ b/src/qml/utils.js @@ -1,7 +1,11 @@ // Copyright 2019 miruka // This file is part of harmonyqml, licensed under LGPLv3. -"use strict" + +function hsluv(hue, saturation, lightness, alpha=1.0) { + let rgb = py.call_sync("APP.backend.hsluv", [hue, saturation, lightness]) + return Qt.rgba(rgb[0], rgb[1], rgb[2], alpha) +} function hsl(hue, saturation, lightness) { @@ -30,31 +34,20 @@ function arrayToModelItem(keysName, array) { function hueFrom(string) { - // Calculate and return a unique hue between 0 and 1 for the string + // Calculate and return a unique hue between 0 and 360 for the string let hue = 0 for (let i = 0; i < string.length; i++) { hue += string.charCodeAt(i) * 99 } - return hue % 360 / 360 -} - - -function avatarColor(name) { - return Qt.hsla( - hueFrom(name), - theme.avatar.background.saturation, - theme.avatar.background.lightness, - theme.avatar.background.alpha - ) + return hue % 360 } function nameColor(name) { - return Qt.hsla( + return hsl( hueFrom(name), - theme.displayName.saturation, - theme.displayName.lightness, - 1 + theme.controls.displayName.saturation, + theme.controls.displayName.lightness, ) } diff --git a/src/themes/Default.qpl b/src/themes/Default.qpl index 75f5cd53..d6a8ac3e 100644 --- a/src/themes/Default.qpl +++ b/src/themes/Default.qpl @@ -2,109 +2,183 @@ // This file is part of harmonyqml, licensed under LGPLv3. // vim: syntax=qml -int minimumSupportedWidth: 240 -int minimumSupportedHeight: 120 -int contentIsWideAbove: 439 +real uiScale: 1.0 /* TODO: Implement correctly, do not change for now */ +real fontScale: uiScale -int minimumSupportedWidthPlusSpacing: 240 + spacing * 2 -int minimumSupportedHeightPlusSpacing: 120 + spacing * 2 +Behavior on uiScale { HNumberAnimation {} } +Behavior on fontScale { HNumberAnimation {} } -int baseElementsHeight: 36 -int spacing: 8 -int radius: 5 -int animationDuration: 100 +int minimumSupportedWidth: 240 * uiScale +int minimumSupportedHeight: 120 * uiScale +int contentIsWideAbove: 439 * uiScale -color pageHeadersBackground: colors.background2 +int minimumSupportedWidthPlusSpacing: minimumSupportedWidth + spacing * 2 +int minimumSupportedHeightPlusSpacing: minimumSupportedHeight + spacing * 2 + +int baseElementsHeight: 36 * uiScale +int spacing: 12 * uiScale +int radius: 5 +int animationDuration: 100 + +string preferredIconPack: "light-thin" fontSize: - int smallest: 6 - int smaller: 8 - int small: 13 - int normal: 16 - int big: 22 - int bigger: 32 - int biggest: 48 + int smaller: 8 * fontScale + int small: 13 * fontScale + int normal: 16 * fontScale + int big: 22 * fontScale + int bigger: 32 * fontScale + int biggest: 48 * fontScale fontFamily: - string sans: "SFNS Display" + string sans: "Roboto" string serif: "Roboto Slab" - string mono: "Hack" + string mono: "Hack" colors: - color background0: Ut.hsla(0, 0, 90, 0.5) - color background1: Ut.hsla(0, 0, 90, 0.6) - color background2: Ut.hsla(0, 0, 90, 0.7) - color foreground: "black" - color foregroundDim: Ut.hsl(0, 0, 20) - color foregroundDim2: Ut.hsl(0, 0, 30) - color foregroundError: Ut.hsl(342, 64, 32) - color textBorder: Ut.hsla(0, 0, 0, 0.07) - color accent: Ut.hsl(25, 60, 50) - color accentDarker: Ut.hsl(25, 60, 35) + int hue: 260 + int saturation: 40 + real intensity: 1.0 + real opacity: 1.0 + + color weakBackground: hsluv(hue, saturation, intensity * 12, opacity) + color mediumBackground: hsluv(hue, saturation, intensity * 9, opacity) + color strongBackground: hsluv(hue, saturation, intensity * 6, opacity) + color fullBackground: hsluv(hue, saturation, intensity * 2, opacity) + + color brightText: hsluv(0, 0, intensity * 100) + color text: hsluv(0, 0, intensity * 80) + color dimText: hsluv(0, 0, intensity * 55) + color dimmerText: hsluv(0, 0, intensity * 30) + color accentText: hsluv(hue - 10, saturation * 2.25, 60) + color link: accentText + color code: hsluv(hue + 5, saturation * 1.5, intensity * 60) + + NumberAnimation on intensity // For testing + running: false + from: 0 + to: 10 + duration: 20000 + loops: Animation.Infinite + controls: + box: + color background: colors.strongBackground + int radius: theme.radius + + header: + color background: colors.mediumBackground + button: - color background: colors.background2 + color background: colors.fullBackground + color text: colors.text + color disabledText: colors.dimmerText interactiveRectangle: color background: "transparent" - color hoveredBackground: Ut.hsla(0, 0, 0, 0.2) - color pressedBackground: Ut.hsla(0, 0, 0, 0.4) - color checkedBackground: Ut.hsla(0, 0, 0, 0.4) + + color hoveredOverlay: hsluv(0, 0, 100) + color pressedOverlay: hsluv(0, 0, 100) + color checkedOverlay: hsluv(0, 0, 100) + + real hoveredOpacity: 0.1 + real pressedOpacity: 0.2 + real checkedOpacity: 0.2 textField: - color background: colors.background2 - color border: "transparent" + color background: colors.fullBackground color focusedBackground: background - color focusedBorder: colors.accent - int borderWidth: 1 + + int borderWidth: 1 + color border: "transparent" + color focusedBorder: colors.accentText + + color text: colors.text + color focusedText: colors.text textArea: - color background: colors.background2 + color background: colors.fullBackground + color text: colors.text + + avatar: + int size: baseElementsHeight + int radius: theme.radius + + background: + int saturation: colors.saturation - 5 + int lightness: Math.min(50, colors.intensity * 20) + real opacity: 1.0 + + letter: + int saturation: colors.saturation * 1.5 + int lightness: colors.intensity * 60 + real opacity: 1.0 + + displayName: + int saturation: 45 + int lightness: 55 + + +ui: + color background: colors.weakBackground + sidePane: - real autoWidthRatio: 0.33 - int maximumAutoWidth: 320 + real autoWidthRatio: 0.33 * uiScale + int maximumAutoWidth: 320 * uiScale - int autoCollapseBelowWidth: 128 - int collapsedWidth: avatar.size + int autoCollapseBelowWidth: 128 * uiScale + int collapsedWidth: controls.avatar.size int autoReduceBelowWindowWidth: minimumSupportedWidthPlusSpacing + collapsedWidth - color background: colors.background2 + color background: colors.strongBackground account: - color background: Qt.lighter(colors.background2, 1.05) + color background: sidePane.background + color name: colors.text + + room: + color background: sidePane.background + color name: colors.text + color subtitle: colors.dimText settingsButton: - color background: colors.background2 + color background: sidePane.background filterRooms: - color background: colors.background2 + color background: sidePane.background + chat: - selectViewBar: - color background: colors.background2 - roomHeader: - color background: colors.background2 + color background: colors.strongBackground + color name: colors.text + color topic: colors.dimText + + roomSidePane: + color background: colors.mediumBackground + + selectViewBar: + color background: chat.roomHeader.background eventList: int ownEventsOnRightUnderWidth: 768 - color background: "transparent" + color background: "transparent" message: - color ownBackground: Ut.hsla(25, 40, 82, 0.7) - color background: colors.background2 - color body: colors.foreground - color date: colors.foregroundDim + int radius: theme.radius + color background: colors.strongBackground + /* color ownBackground: hsluv(25, 40, 82, colors.opacity) */ + color ownBackground: background + color body: colors.text + color date: colors.dimText - color link: colors.accentDarker - // color code: Ut.hsl(0, 0, 80) - // color codeBackground: Ut.hsl(0, 0, 10) - color code: Ut.hsl(265, 60, 35) - color greenText: Ut.hsl(80, 60, 25) + color greenText: hsluv(80, 60, 25) + color link: colors.link + color code: colors.code string styleSheet: "a { color: " + link + " }" + @@ -112,8 +186,13 @@ chat: "code { font-family: " + fontFamily.mono + "; " + "color: " + code + " }" + - "h1, h2 { font-weight: normal }" + - "h6 { font-size: small }" + + "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 }" + ".greentext { color: " + greenText + " }" @@ -121,40 +200,21 @@ chat: '\n' daybreak: - color background: colors.background2 - color foreground: colors.foreground - int radius: theme.radius + color background: colors.strongBackground + color text: colors.text + int radius: theme.radius inviteBanner: - color background: colors.background2 + color background: colors.mediumBackground leftBanner: - color background: colors.background2 + color background: colors.mediumBackground unknownDevices: - color background: colors.background2 + color background: colors.mediumBackground typingMembers: - color background: colors.background1 + color background: colors.mediumBackground sendBox: - color background: colors.background2 - -box: - color background: colors.background0 - int radius: theme.radius - -avatar: - int size: baseElementsHeight - int radius: theme.radius - color letter: "white" - - background: - real saturation: 0.22 - real lightness: 0.5 - real alpha: 1 - color unknown: Ut.hsl(0, 0, 22) - -displayName: - real saturation: 0.32 - real lightness: 0.3 + color background: colors.strongBackground