Merge branch 'dev' into 'main'
Merge version 0.7.3 See merge request mx-moment/moment!6
34
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
@ -1,34 +0,0 @@
|
||||||
---
|
|
||||||
name: Bug report
|
|
||||||
about: 'Report an unexpected behavior '
|
|
||||||
title: ''
|
|
||||||
labels: bug
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Description
|
|
||||||
|
|
||||||
Describe your issue in details, provide error logs or screenshots if possible.
|
|
||||||
|
|
||||||
### Your environment
|
|
||||||
|
|
||||||
- OS or distribution (e.g. Arch Linux, macOS 10.15, Windows 7...)
|
|
||||||
- Architecture (e.g. x86 64bit)
|
|
||||||
- For Linux users: your desktop environment or window manager (e.g. GNOME 3.34 Wayland, i3 4.17, etc)
|
|
||||||
- How did you install Mirage? (e.g. manual build, distribution repository, AppImage, Flatpak...)
|
|
||||||
- For manual installations: your Qt version
|
|
||||||
- For manual installations: your Python version
|
|
||||||
|
|
||||||
### Steps to reproduce
|
|
||||||
|
|
||||||
1. Do this...
|
|
||||||
2. Do that...
|
|
||||||
|
|
||||||
### Expected behavior
|
|
||||||
|
|
||||||
Tell us what should happen
|
|
||||||
|
|
||||||
### Actual behavior
|
|
||||||
|
|
||||||
Tell us what happens instead
|
|
10
.github/ISSUE_TEMPLATE/feature_request.md
vendored
|
@ -1,10 +0,0 @@
|
||||||
---
|
|
||||||
name: Feature request
|
|
||||||
about: Suggest an idea for the application or project
|
|
||||||
title: ''
|
|
||||||
labels: enhancement
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
10
.github/ISSUE_TEMPLATE/question.md
vendored
|
@ -1,10 +0,0 @@
|
||||||
---
|
|
||||||
name: Question
|
|
||||||
about: Ask a question about the application or project
|
|
||||||
title: ''
|
|
||||||
labels: question
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
2
.gitignore
vendored
|
@ -16,6 +16,8 @@ dist
|
||||||
Makefile
|
Makefile
|
||||||
mirage
|
mirage
|
||||||
mirage.pro.user
|
mirage.pro.user
|
||||||
|
moment
|
||||||
|
moment.pro.user
|
||||||
*.AppImage
|
*.AppImage
|
||||||
|
|
||||||
tags
|
tags
|
||||||
|
|
17
README.md
|
@ -1,5 +1,4 @@
|
||||||
# Moment
|
# Moment
|
||||||
https://img.shields.io/gitlab/v/release/mx-moment/moment
|
|
||||||
[![Latest release](https://img.shields.io/gitlab/v/release/mx-moment/moment)](https://gitlab.com/mx-moment/moment/-/releases)
|
[![Latest release](https://img.shields.io/gitlab/v/release/mx-moment/moment)](https://gitlab.com/mx-moment/moment/-/releases)
|
||||||
[![Built with matrix-nio](https://img.shields.io/badge/built%20with-matrix--nio-brightgreen)](https://github.com/poljar/matrix-nio)
|
[![Built with matrix-nio](https://img.shields.io/badge/built%20with-matrix--nio-brightgreen)](https://github.com/poljar/matrix-nio)
|
||||||
[![#moment-client:matrix.org](https://img.shields.io/matrix/moment-client:matrix.org?color=blueviolet)](https://matrix.to/#/#moment-client:matrix.org)
|
[![#moment-client:matrix.org](https://img.shields.io/matrix/moment-client:matrix.org?color=blueviolet)](https://matrix.to/#/#moment-client:matrix.org)
|
||||||
|
@ -20,7 +19,7 @@ Written in Qt/QML and Python, **currently in alpha**.
|
||||||
Moment is based on [Mirage](https://github.com/mirukana/mirage),
|
Moment is based on [Mirage](https://github.com/mirukana/mirage),
|
||||||
see differences [here](docs/MIRAGEDIFF.md).
|
see differences [here](docs/MIRAGEDIFF.md).
|
||||||
|
|
||||||
![Chat screenshot](docs/screenshots/01-chat.png?raw=true)
|
![Chat screenshot](docs/screenshots/m01-chat.png?raw=true)
|
||||||
|
|
||||||
## Currently Implemented Features
|
## Currently Implemented Features
|
||||||
|
|
||||||
|
@ -96,10 +95,10 @@ see differences [here](docs/MIRAGEDIFF.md).
|
||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||
|
|
||||||
![Sign-in](docs/screenshots/02-sign-in.png)
|
![Sign-in](docs/screenshots/m02-sign-in.png)
|
||||||
![Account settings](docs/screenshots/03-account-settings.png)
|
![Account settings](docs/screenshots/m03-account-settings.png)
|
||||||
![Room creation](docs/screenshots/04-create-room.png)
|
![Room creation](docs/screenshots/m04-create-room.png)
|
||||||
![Chat](docs/screenshots/01-chat.png?raw=true)
|
![Chat](docs/screenshots/m01-chat.png?raw=true)
|
||||||
![Main pane in small window](docs/screenshots/05-main-pane-small.png)
|
![Main pane in small window](docs/screenshots/m05-main-pane-small.png)
|
||||||
![Chat in small window](docs/screenshots/06-chat-small.png)
|
![Chat in small window](docs/screenshots/m06-chat-small.png)
|
||||||
![Room pane in small window](docs/screenshots/07-room-pane-small.png)
|
![Room pane in small window](docs/screenshots/m07-room-pane-small.png)
|
||||||
|
|
|
@ -6,6 +6,7 @@ The format is based on
|
||||||
and this project adheres to
|
and this project adheres to
|
||||||
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
- [0.7.3 (2022-01-24)](#073-2022-01-24)
|
||||||
- [0.7.2 (2021-07-26)](#072-2021-07-26)
|
- [0.7.2 (2021-07-26)](#072-2021-07-26)
|
||||||
- [0.7.1 (2021-03-04)](#071-2021-03-04)
|
- [0.7.1 (2021-03-04)](#071-2021-03-04)
|
||||||
- [0.7.0 (2021-02-28)](#070-2021-02-28)
|
- [0.7.0 (2021-02-28)](#070-2021-02-28)
|
||||||
|
@ -23,6 +24,44 @@ and this project adheres to
|
||||||
- [0.4.0 (2020-03-21)](#040-2020-03-21)
|
- [0.4.0 (2020-03-21)](#040-2020-03-21)
|
||||||
|
|
||||||
|
|
||||||
|
## 0.7.3 (2022-01-24)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- [Keybindings page](KEYBINDINGS.md)
|
||||||
|
|
||||||
|
- When first starting Moment, offer to migrate config
|
||||||
|
|
||||||
|
- Foliage theme
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Renamed Mirage to Moment
|
||||||
|
|
||||||
|
- Default config, cache and data directories
|
||||||
|
|
||||||
|
- Default keybinds:
|
||||||
|
- quit is now Ctrl+Q
|
||||||
|
- reply is now Ctrl+R
|
||||||
|
- remove is now Ctrl+Shift+R
|
||||||
|
- "focus filter" is now Ctrl+K
|
||||||
|
- "previous message" is now Ctrl+I
|
||||||
|
|
||||||
|
- In-app links lead to
|
||||||
|
[the Moment repository](https://gitlab.com/mx-moment/moment)
|
||||||
|
|
||||||
|
- Default theme is now Foliage.qpl
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
- Appimage: remove Appimage installation method
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Compatibility with Python 3.10
|
||||||
|
|
||||||
|
- Minor UI fixes
|
||||||
|
|
||||||
## 0.7.2 (2021-07-26)
|
## 0.7.2 (2021-07-26)
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
|
@ -42,7 +42,7 @@ By sending your changes, you agree to license them under the LGPL 3.0 or later.
|
||||||
|
|
||||||
### Procedure
|
### Procedure
|
||||||
|
|
||||||
Start by forking the main repository from GitHub, then
|
Start by forking the main repository from GitLab, then
|
||||||
clone your fork and switch to a new branch based on `dev`, in which
|
clone your fork and switch to a new branch based on `dev`, in which
|
||||||
you will make your changes:
|
you will make your changes:
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ You will then be able to make a pull request by going
|
||||||
to the [main repo](https://gitlab.com/mx-moment/moment).
|
to the [main repo](https://gitlab.com/mx-moment/moment).
|
||||||
|
|
||||||
Once your pull request is merged, you can update `dev`, and delete your
|
Once your pull request is merged, you can update `dev`, and delete your
|
||||||
GitHub and local branch:
|
GitLab and local branch:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git fetch upstream
|
git fetch upstream
|
||||||
|
|
|
@ -5,7 +5,6 @@ but compiling on Windows and macOS should be possible with the right tools.
|
||||||
|
|
||||||
- [Packages](#packages)
|
- [Packages](#packages)
|
||||||
- [Linux](#linux)
|
- [Linux](#linux)
|
||||||
- [AppImage](#appimage)
|
|
||||||
- [Flatpak](#flatpak)
|
- [Flatpak](#flatpak)
|
||||||
- [Alpine Linux / postmarketOS](#alpine-linux--postmarketOS)
|
- [Alpine Linux / postmarketOS](#alpine-linux--postmarketOS)
|
||||||
- [Arch Linux](#arch-linux)
|
- [Arch Linux](#arch-linux)
|
||||||
|
@ -37,22 +36,9 @@ but compiling on Windows and macOS should be possible with the right tools.
|
||||||
|
|
||||||
For developement, or if none of the package options are satisfying,
|
For developement, or if none of the package options are satisfying,
|
||||||
see [manual installation](#manual-installation).
|
see [manual installation](#manual-installation).
|
||||||
Packages other than the AppImage and Flatpak are not maintained by the Mirage
|
Packages other than the Flatpak are not maintained by the Mirage
|
||||||
authors, and thus might be outdated.
|
authors, and thus might be outdated.
|
||||||
|
|
||||||
#### AppImage (Leftover instructions from Mirage; not supported for Moment)
|
|
||||||
|
|
||||||
For **x86 64bit glibc-based systems**, Mirage is available as an AppImage
|
|
||||||
on the [release page](https://github.com/mirukana/mirage/releases).
|
|
||||||
|
|
||||||
AppImages are single executable files that contain the app and all
|
|
||||||
its dependencies.
|
|
||||||
Mirage images are built in Ubuntu 16.04, and should therefore run on any distro
|
|
||||||
released in April 2016 or later.
|
|
||||||
|
|
||||||
[How to start AppImages](https://docs.appimage.org/introduction/quickstart.html#how-to-run-an-appimage)
|
|
||||||
(TL;DR: `chmod +x Mirage-*.AppImage && ./Mirage-*.AppImage`)
|
|
||||||
|
|
||||||
#### Flatpak (Leftover instructions from Mirage; not supported for Moment)
|
#### Flatpak (Leftover instructions from Mirage; not supported for Moment)
|
||||||
|
|
||||||
Mirage is also available as a Flatpak.
|
Mirage is also available as a Flatpak.
|
||||||
|
|
|
@ -6,114 +6,114 @@ Keybindings as defined in [the default configuration file](src/config/settings.p
|
||||||
|
|
||||||
Key | Function
|
Key | Function
|
||||||
------ | ------
|
------ | ------
|
||||||
<kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>C</kbd> | Toggle compact interface
|
<kbd>Ctrl + Alt + C</kbd> | Toggle compact interface
|
||||||
<kbd>Ctrl</kbd> + <kbd>+</kbd> | Zoom in
|
<kbd>Ctrl + +</kbd> | Zoom in
|
||||||
<kbd>Ctrl</kbd> + <kbd>-</kbd> | Zoom out
|
<kbd>Ctrl + -</kbd> | Zoom out
|
||||||
<kbd>Ctrl</kbd> + <kbd>=</kbd> | Reset zoom
|
<kbd>Ctrl + =</kbd> | Reset zoom
|
||||||
<kbd>Alt</kbd> + <kbd>Shift</kbd> + <kbd>Left</kbd> <br> <kbd>Alt</kbd> + <kbd>Shift</kbd> + <kbd>H</kbd> | Previous tab
|
<kbd>Alt + Shift + Left</kbd> <br> <kbd>Alt + Shift + H</kbd> | Previous tab
|
||||||
<kbd>Alt</kbd> + <kbd>Shift</kbd> + <kbd>Right</kbd> <br> <kbd>Alt</kbd> + <kbd>Shift</kbd> + <kbd>L</kbd> | Next tab
|
<kbd>Alt + Shift + Right</kbd> <br> <kbd>Alt + Shift + L</kbd> | Next tab
|
||||||
<kbd>Ctrl</kbd> + <kbd>Tab</kbd> | Switch to the last opened page
|
<kbd>Ctrl + Tab</kbd> | Switch to the last opened page
|
||||||
<kbd>Ctrl</kbd> + <kbd>H</kbd> | Earlier page in history (page back)
|
<kbd>Ctrl + H</kbd> | Earlier page in history (page back)
|
||||||
<kbd>Ctrl</kbd> + <kbd>L</kbd> | Later page in history (page forward)
|
<kbd>Ctrl + L</kbd> | Later page in history (page forward)
|
||||||
<kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>H</kbd> | Toggle notifications, except highlights
|
<kbd>Ctrl + Alt + H</kbd> | Toggle notifications, except highlights
|
||||||
<kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>N</kbd> | Toggle notifications
|
<kbd>Ctrl + Alt + N</kbd> | Toggle notifications
|
||||||
<kbd>F1</kbd> | QML developer console
|
<kbd>F1</kbd> | QML developer console
|
||||||
<kbd>Shift</kbd> + <kbd>F1</kbd> | Python debugger
|
<kbd>Shift + F1</kbd> | Python debugger
|
||||||
<kbd>Alt</kbd> + <kbd>F1</kbd> | Python remote debugger
|
<kbd>Alt + F1</kbd> | Python remote debugger
|
||||||
<kbd>Ctrl</kbd> + <kbd>Q</kbd> | Quit Moment *
|
<kbd>Ctrl + Q</kbd> | Quit Moment *
|
||||||
|
|
||||||
## Scrolling bindings
|
## Scrolling bindings
|
||||||
|
|
||||||
Key | Function
|
Key | Function
|
||||||
------ | ------
|
------ | ------
|
||||||
<kbd>Alt</kbd> + <kbd>Up</kbd> <br> <kbd>Alt</kbd> + <kbd>K</kbd> | Scroll up
|
<kbd>Alt + Up</kbd> <br> <kbd>Alt + K</kbd> | Scroll up
|
||||||
<kbd>Alt</kbd> + <kbd>Down</kbd> <br> <kbd>Alt</kbd> + <kbd>J</kbd> | Scroll down
|
<kbd>Alt + Down</kbd> <br> <kbd>Alt + J</kbd> | Scroll down
|
||||||
<kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>Up</kbd> <br> <kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>K</kbd> <br> <kbd>PgUp</kbd> | Page up
|
<kbd>Ctrl + Alt + Up</kbd> <br> <kbd>Ctrl + Alt + K</kbd> <br> <kbd>PgUp</kbd> | Page up
|
||||||
<kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>Down</kbd> <br> <kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>J</kbd> <br> <kbd>PgDown</kbd> | Page down
|
<kbd>Ctrl + Alt + Down</kbd> <br> <kbd>Ctrl + Alt + J</kbd> <br> <kbd>PgDown</kbd> | Page down
|
||||||
<kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>Shift</kbd> + <kbd>Up</kbd> <br> <kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>Shift</kbd> + <kbd>K</kbd> <br> <kbd>Home</kbd> | Scroll to top
|
<kbd>Ctrl + Alt + Shift + Up</kbd> <br> <kbd>Ctrl + Alt + Shift + K</kbd> <br> <kbd>Home</kbd> | Scroll to top
|
||||||
<kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>Shift</kbd> + <kbd>Down</kbd> <br> <kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>Shift</kbd> + <kbd>J</kbd> <br> <kbd>End</kbd> | Scroll to bottom
|
<kbd>Ctrl + Alt + Shift + Down</kbd> <br> <kbd>Ctrl + Alt + Shift + J</kbd> <br> <kbd>End</kbd> | Scroll to bottom
|
||||||
|
|
||||||
## Account bindings
|
## Account bindings
|
||||||
|
|
||||||
Key | Function
|
Key | Function
|
||||||
------ | ------
|
------ | ------
|
||||||
<kbd>Alt</kbd> + <kbd>Shift</kbd> + <kbd>A</kbd> | Add new account
|
<kbd>Alt + Shift + A</kbd> | Add new account
|
||||||
<kbd>Alt</kbd> + <kbd>O</kbd> | Collapse current account
|
<kbd>Alt + O</kbd> | Collapse current account
|
||||||
<kbd>Alt</kbd> + <kbd>A</kbd> | Current account settings
|
<kbd>Alt + A</kbd> | Current account settings
|
||||||
<kbd>Alt</kbd> + <kbd>P</kbd> | Current account context menu
|
<kbd>Alt + P</kbd> | Current account context menu
|
||||||
<kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>U</kbd> <br> <kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>A</kbd> | Unavailable status
|
<kbd>Ctrl + Alt + U</kbd> <br> <kbd>Ctrl + Alt + A</kbd> | Unavailable status
|
||||||
<kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>I</kbd> | Invisible status
|
<kbd>Ctrl + Alt + I</kbd> | Invisible status
|
||||||
<kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>O</kbd> | Offline status
|
<kbd>Ctrl + Alt + O</kbd> | Offline status
|
||||||
<kbd>Alt</kbd> + <kbd>Shift</kbd> + <kbd>N</kbd> | Previous account
|
<kbd>Alt + Shift + N</kbd> | Previous account
|
||||||
<kbd>Alt</kbd> + <kbd>N</kbd> | Next account
|
<kbd>Alt + N</kbd> | Next account
|
||||||
<kbd>Ctrl</kbd> + <kbd>1</kbd> | Switch to account 1
|
<kbd>Ctrl + 1</kbd> | Switch to account 1
|
||||||
<kbd>Ctrl</kbd> + <kbd>2</kbd> | Switch to account 2
|
<kbd>Ctrl + 2</kbd> | Switch to account 2
|
||||||
<kbd>Ctrl</kbd> + <kbd>3</kbd> | Switch to account 3
|
<kbd>Ctrl + 3</kbd> | Switch to account 3
|
||||||
<kbd>Ctrl</kbd> + <kbd>4</kbd> | Switch to account 4
|
<kbd>Ctrl + 4</kbd> | Switch to account 4
|
||||||
<kbd>Ctrl</kbd> + <kbd>5</kbd> | Switch to account 5
|
<kbd>Ctrl + 5</kbd> | Switch to account 5
|
||||||
<kbd>Ctrl</kbd> + <kbd>6</kbd> | Switch to account 6
|
<kbd>Ctrl + 6</kbd> | Switch to account 6
|
||||||
<kbd>Ctrl</kbd> + <kbd>7</kbd> | Switch to account 7
|
<kbd>Ctrl + 7</kbd> | Switch to account 7
|
||||||
<kbd>Ctrl</kbd> + <kbd>8</kbd> | Switch to account 8
|
<kbd>Ctrl + 8</kbd> | Switch to account 8
|
||||||
<kbd>Ctrl</kbd> + <kbd>9</kbd> | Switch to account 9
|
<kbd>Ctrl + 9</kbd> | Switch to account 9
|
||||||
<kbd>Ctrl</kbd> + <kbd>0</kbd> | Switch to account 10
|
<kbd>Ctrl + 0</kbd> | Switch to account 10
|
||||||
|
|
||||||
## Room bindings
|
## Room bindings
|
||||||
|
|
||||||
Key | Function
|
Key | Function
|
||||||
------ | ------
|
------ | ------
|
||||||
<kbd>Alt</kbd> + <kbd>C</kbd> | Create a new room (start chat)
|
<kbd>Alt + C</kbd> | Create a new room (start chat)
|
||||||
<kbd>Alt</kbd> + <kbd>F</kbd> | Focus filter
|
<kbd>Alt + F</kbd> <br> <kbd>Ctrl + K</kbd> | Focus filter *
|
||||||
<kbd>Alt</kbd> + <kbd>Shift</kbd> + <kbd>F</kbd> | Clear filter
|
<kbd>Alt + Shift + F</kbd> | Clear filter
|
||||||
<kbd>Alt</kbd> + <kbd>Shift</kbd> + <kbd>Up</kbd> <br> <kbd>Alt</kbd> + <kbd>Shift</kbd> + <kbd>K</kbd> | Previous room
|
<kbd>Alt + Shift + Up</kbd> <br> <kbd>Alt + Shift + K</kbd> | Previous room
|
||||||
<kbd>Alt</kbd> + <kbd>Shift</kbd> + <kbd>Down</kbd> <br> <kbd>Alt</kbd> + <kbd>Shift</kbd> + <kbd>J</kbd> | Next room
|
<kbd>Alt + Shift + Down</kbd> <br> <kbd>Alt + Shift + J</kbd> | Next room
|
||||||
<kbd>Alt</kbd> + <kbd>Shift</kbd> + <kbd>U</kbd> | Previous unread
|
<kbd>Alt + Shift + U</kbd> | Previous unread
|
||||||
<kbd>Alt</kbd> + <kbd>U</kbd> | Next unread
|
<kbd>Alt + U</kbd> | Next unread
|
||||||
<kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>U</kbd> | Oldest unread
|
<kbd>Ctrl + Shift + U</kbd> | Oldest unread
|
||||||
<kbd>Ctrl</kbd> + <kbd>U</kbd> | Latest unread
|
<kbd>Ctrl + U</kbd> | Latest unread
|
||||||
<kbd>Alt</kbd> + <kbd>Shift</kbd> + <kbd>M</kbd> | Previous highlight
|
<kbd>Alt + Shift + M</kbd> | Previous highlight
|
||||||
<kbd>Alt</kbd> + <kbd>M</kbd> | Next highlight
|
<kbd>Alt + M</kbd> | Next highlight
|
||||||
<kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>M</kbd> | Oldest highlight
|
<kbd>Ctrl + Shift + M</kbd> | Oldest highlight
|
||||||
<kbd>Ctrl</kbd> + <kbd>M</kbd> | Latest highlight
|
<kbd>Ctrl + M</kbd> | Latest highlight
|
||||||
<kbd>Alt</kbd> + <kbd>1</kbd> | Room number 1 in account
|
<kbd>Alt + 1</kbd> | Room number 1 in account
|
||||||
<kbd>Alt</kbd> + <kbd>2</kbd> | Room number 2 in account
|
<kbd>Alt + 2</kbd> | Room number 2 in account
|
||||||
<kbd>Alt</kbd> + <kbd>3</kbd> | Room number 3 in account
|
<kbd>Alt + 3</kbd> | Room number 3 in account
|
||||||
<kbd>Alt</kbd> + <kbd>4</kbd> | Room number 4 in account
|
<kbd>Alt + 4</kbd> | Room number 4 in account
|
||||||
<kbd>Alt</kbd> + <kbd>5</kbd> | Room number 5 in account
|
<kbd>Alt + 5</kbd> | Room number 5 in account
|
||||||
<kbd>Alt</kbd> + <kbd>6</kbd> | Room number 6 in account
|
<kbd>Alt + 6</kbd> | Room number 6 in account
|
||||||
<kbd>Alt</kbd> + <kbd>7</kbd> | Room number 7 in account
|
<kbd>Alt + 7</kbd> | Room number 7 in account
|
||||||
<kbd>Alt</kbd> + <kbd>8</kbd> | Room number 8 in account
|
<kbd>Alt + 8</kbd> | Room number 8 in account
|
||||||
<kbd>Alt</kbd> + <kbd>9</kbd> | Room number 9 in account
|
<kbd>Alt + 9</kbd> | Room number 9 in account
|
||||||
<kbd>Alt</kbd> + <kbd>0</kbd> | Room number 10 in account
|
<kbd>Alt + 0</kbd> | Room number 10 in account
|
||||||
(no binding) | Jump to specific room by ID
|
(no binding) | Jump to specific room by ID
|
||||||
|
|
||||||
## Chat bindings
|
## Chat bindings
|
||||||
|
|
||||||
Key | Function
|
Key | Function
|
||||||
------ | ------
|
------ | ------
|
||||||
<kbd>Alt</kbd> + <kbd>R</kbd> | Focus room pane
|
<kbd>Alt + R</kbd> | Focus room pane
|
||||||
<kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>R</kbd> | Hide room pane
|
<kbd>Ctrl + Alt + R</kbd> | Hide room pane
|
||||||
<kbd>Alt</kbd> + <kbd>I</kbd> | Invite members
|
<kbd>Alt + I</kbd> | Invite members
|
||||||
<kbd>Alt</kbd> + <kbd>Escape</kbd> | Leave current chat
|
<kbd>Alt + Escape</kbd> | Leave current chat
|
||||||
<kbd>Alt</kbd> + <kbd>S</kbd> | Upload file
|
<kbd>Alt + S</kbd> | Upload file
|
||||||
<kbd>Alt</kbd> + <kbd>Shift</kbd> + <kbd>S</kbd> | Send file at clipboard path
|
<kbd>Alt + Shift + S</kbd> | Send file at clipboard path
|
||||||
|
|
||||||
## Message bindings
|
## Message bindings
|
||||||
|
|
||||||
Key | Function
|
Key | Function
|
||||||
------ | ------
|
------ | ------
|
||||||
<kbd>Ctrl</kbd> + <kbd>Up</kbd> <br> <kbd>Ctrl</kbd> + <kbd>K</kbd> | Focus previous message
|
<kbd>Ctrl + Up</kbd> <br> <kbd>Ctrl + I</kbd> | Focus previous message *
|
||||||
<kbd>Ctrl</kbd> + <kbd>Down</kbd> <br> <kbd>Ctrl</kbd> + <kbd>J</kbd> | Focus next message
|
<kbd>Ctrl + Down</kbd> <br> <kbd>Ctrl + J</kbd> | Focus next message
|
||||||
<kbd>Ctrl</kbd> + <kbd>Space</kbd> | Select focused message
|
<kbd>Ctrl + Space</kbd> | Select focused message
|
||||||
<kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>Space</kbd> | Select messages until here
|
<kbd>Ctrl + Shift + Space</kbd> | Select messages until here
|
||||||
<kbd>Ctrl</kbd> + <kbd>D</kbd> | Unfocus or deselect
|
<kbd>Ctrl + D</kbd> | Unfocus or deselect
|
||||||
<kbd>Ctrl</kbd> + <kbd>S</kbd> | Display seen tooltips
|
<kbd>Ctrl + S</kbd> | Display seen tooltips
|
||||||
<kbd>Ctrl</kbd> + <kbd>R</kbd> <br> <kbd>Alt</kbd> + <kbd>Del</kbd> | Remove message
|
<kbd>Ctrl + Shift + R</kbd> <br> <kbd>Alt + Del</kbd> | Remove message *
|
||||||
<kbd>Ctrl</kbd> + <kbd>Q</kbd> | Reply
|
<kbd>Ctrl + R</kbd> | Reply *
|
||||||
<kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>D</kbd> | Debug message
|
<kbd>Ctrl + Shift + D</kbd> | Debug message
|
||||||
<kbd>Ctrl</kbd> + <kbd>O</kbd> | Open link/file in message
|
<kbd>Ctrl + O</kbd> | Open link/file in message
|
||||||
<kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>O</kbd> | Open link/file externally
|
<kbd>Ctrl + Shift + O</kbd> | Open link/file externally
|
||||||
<kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>C</kbd> | Copy downloaded file path
|
<kbd>Ctrl + Shift + C</kbd> | Copy downloaded file path
|
||||||
<kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>L</kbd> | Clear messages
|
<kbd>Ctrl + Shift + L</kbd> | Clear messages
|
||||||
|
|
||||||
## Image viewer bindings
|
## Image viewer bindings
|
||||||
|
|
||||||
|
@ -121,20 +121,20 @@ Key | Function
|
||||||
------ | ------
|
------ | ------
|
||||||
<kbd>X</kbd> <br> <kbd>Q</kbd> | Close image viewer
|
<kbd>X</kbd> <br> <kbd>Q</kbd> | Close image viewer
|
||||||
<kbd>E</kbd> | Expand image viewer
|
<kbd>E</kbd> | Expand image viewer
|
||||||
<kbd>F</kbd> <br> <kbd>F11</kbd> <br> <kbd>Alt</kbd> + <kbd>Return</kbd> <br> <kbd>Alt</kbd> + <kbd>Enter</kbd> | Fullscreen image viewer
|
<kbd>F</kbd> <br> <kbd>F11</kbd> <br> <kbd>Alt + Return</kbd> <br> <kbd>Alt + Enter</kbd> | Fullscreen image viewer
|
||||||
<kbd>H</kbd> <br> <kbd>Left</kbd> <br> <kbd>Alt</kbd> + <kbd>H</kbd> <br> <kbd>Alt</kbd> + <kbd>Left</kbd> | Pan image left
|
<kbd>H</kbd> <br> <kbd>Left</kbd> <br> <kbd>Alt + H</kbd> <br> <kbd>Alt + Left</kbd> | Pan image left
|
||||||
<kbd>J</kbd> <br> <kbd>Down</kbd> <br> <kbd>Alt</kbd> + <kbd>J</kbd> <br> <kbd>Alt</kbd> + <kbd>Down</kbd> | Pan image down
|
<kbd>J</kbd> <br> <kbd>Down</kbd> <br> <kbd>Alt + J</kbd> <br> <kbd>Alt + Down</kbd> | Pan image down
|
||||||
<kbd>K</kbd> <br> <kbd>Up</kbd> <br> <kbd>Alt</kbd> + <kbd>K</kbd> <br> <kbd>Alt</kbd> + <kbd>Up</kbd> | Pan image up
|
<kbd>K</kbd> <br> <kbd>Up</kbd> <br> <kbd>Alt + K</kbd> <br> <kbd>Alt + Up</kbd> | Pan image up
|
||||||
<kbd>L</kbd> <br> <kbd>Right</kbd> <br> <kbd>Alt</kbd> + <kbd>L</kbd> <br> <kbd>Alt</kbd> + <kbd>Right</kbd> | Pan image right
|
<kbd>L</kbd> <br> <kbd>Right</kbd> <br> <kbd>Alt + L</kbd> <br> <kbd>Alt + Right</kbd> | Pan image right
|
||||||
<kbd>Z</kbd> <br> <kbd>+</kbd> <br> <kbd>Ctrl</kbd> + <kbd>+</kbd> | Zoom in
|
<kbd>Z</kbd> <br> <kbd>+</kbd> <br> <kbd>Ctrl + +</kbd> | Zoom in
|
||||||
<kbd>Shift</kbd> + <kbd>Z</kbd> <br> <kbd>-</kbd> <br> <kbd>Ctrl</kbd> + <kbd>-</kbd> | Zoom out
|
<kbd>Shift + Z</kbd> <br> <kbd>-</kbd> <br> <kbd>Ctrl + -</kbd> | Zoom out
|
||||||
<kbd>Alt</kbd> + <kbd>Z</kbd> <br> <kbd>=</kbd> <br> <kbd>Ctrl</kbd> + <kbd>=</kbd> | Reset zoom
|
<kbd>Alt + Z</kbd> <br> <kbd>=</kbd> <br> <kbd>Ctrl + =</kbd> | Reset zoom
|
||||||
<kbd>R</kbd> | Rotate image right
|
<kbd>R</kbd> | Rotate image right
|
||||||
<kbd>Shift</kbd> + <kbd>R</kbd> | Rotate image left
|
<kbd>Shift + R</kbd> | Rotate image left
|
||||||
<kbd>Alt</kbd> + <kbd>R</kbd> | Reset image rotation
|
<kbd>Alt + R</kbd> | Reset image rotation
|
||||||
<kbd>S</kbd> | Speed up gif
|
<kbd>S</kbd> | Speed up gif
|
||||||
<kbd>Shift</kbd> + <kbd>S</kbd> | Slow down gif
|
<kbd>Shift + S</kbd> | Slow down gif
|
||||||
<kbd>Alt</kbd> + <kbd>S</kbd> | Reset gif speed
|
<kbd>Alt + S</kbd> | Reset gif speed
|
||||||
<kbd>Space</kbd> | Pause gif
|
<kbd>Space</kbd> | Pause gif
|
||||||
|
|
||||||
## Security tab bindings
|
## Security tab bindings
|
||||||
|
@ -142,8 +142,10 @@ Key | Function
|
||||||
Key | Function
|
Key | Function
|
||||||
------ | ------
|
------ | ------
|
||||||
<kbd>Tab</kbd> | Navigate next
|
<kbd>Tab</kbd> | Navigate next
|
||||||
<kbd>Shift</kbd> + <kbd>Tab</kbd> | Navigate previous
|
<kbd>Shift + Tab</kbd> | Navigate previous
|
||||||
<kbd>Space</kbd> | Toggle check
|
<kbd>Space</kbd> | Toggle check
|
||||||
<kbd>Menu</kbd> | Session context menu
|
<kbd>Menu</kbd> | Session context menu
|
||||||
<kbd>Alt</kbd> + <kbd>R</kbd> <br> <kbd>F5</kbd> | Refresh session list
|
<kbd>Alt + R</kbd> <br> <kbd>F5</kbd> | Refresh session list
|
||||||
<kbd>Alt</kbd> + <kbd>S</kbd> <br> <kbd>Delete</kbd> | Sign out session
|
<kbd>Alt + S</kbd> <br> <kbd>Delete</kbd> | Sign out session
|
||||||
|
|
||||||
|
*Binding different than in Mirage
|
||||||
|
|
69
docs/MIRAGEDIFF.md
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
# Differences between Moment and Mirage
|
||||||
|
|
||||||
|
## Maintenance
|
||||||
|
|
||||||
|
Due to access issues, it is unlikely
|
||||||
|
<a href="https://github.com/mirukana/mirage">Mirage</a>
|
||||||
|
will continue being maintained. You should check the
|
||||||
|
<a href="https://github.com/mirukana/mirage/commits/master">
|
||||||
|
date of the last commit</a> to make sure.
|
||||||
|
|
||||||
|
## UI
|
||||||
|
|
||||||
|
Moment adds a new default theme. If you would rather have the default
|
||||||
|
Mirage theme instead, add this to `~/.config/moment/settings.py`:
|
||||||
|
``` python
|
||||||
|
class General:
|
||||||
|
theme: str = "Midnight.qpl"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
There are currently no differences in features,
|
||||||
|
although Moment does have some bug fixes that Mirage does not.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Moment has a different configuration directory
|
||||||
|
(you will get an option to migrate your Mirage
|
||||||
|
config automatically on first startup).
|
||||||
|
|
||||||
|
Moment has different default keybindings:
|
||||||
|
- <kbd>Ctrl + Q</kbd> is `Quit`
|
||||||
|
- `Reply` is <kbd>Ctrl + R</kbd> instead of <kbd>Ctrl + Q</kbd>
|
||||||
|
- `Remove` is <kbd>Ctrl + Shift + R</kbd> instead of <kbd>Ctrl + R</kbd>
|
||||||
|
- <kbd>Ctrl + K</kbd> is `Focus filter`
|
||||||
|
- `Focus previous message` is <kbd>Ctrl + I</kbd> instead of <kbd>Ctrl + K</kbd>
|
||||||
|
|
||||||
|
If you wish to have `Focus previous message` and `Focus next message`
|
||||||
|
adjacent on <kbd>Ctrl + U</kbd> and <kbd>Ctrl + I</kbd>,
|
||||||
|
we recommend swapping <kbd>Ctrl + U</kbd> and <kbd>Ctrl + J</kbd>
|
||||||
|
by adding this to `~/.config/moment/settings.py`:
|
||||||
|
``` python
|
||||||
|
class Keys:
|
||||||
|
|
||||||
|
class Rooms:
|
||||||
|
latest_unread = ["Ctrl+J"]
|
||||||
|
|
||||||
|
class Messages:
|
||||||
|
next = ["Ctrl+Down", "Ctrl+U"]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Reverting to default Mirage bindings
|
||||||
|
|
||||||
|
If you wish to revert to default Mirage bindings,
|
||||||
|
add this to `~/.config/moment/settings.py`:
|
||||||
|
``` python
|
||||||
|
class Keys:
|
||||||
|
|
||||||
|
quit = []
|
||||||
|
|
||||||
|
class Rooms:
|
||||||
|
focus_filter = ["Alt+F"]
|
||||||
|
latest_unread = ["Ctrl+J"]
|
||||||
|
|
||||||
|
class Messages:
|
||||||
|
reply = ["Ctrl+Q"]
|
||||||
|
remove = ["Ctrl+R"]
|
||||||
|
previous = ["Ctrl+Up", "Ctrl+K"]
|
||||||
|
```
|
|
@ -11,7 +11,7 @@ for example:
|
||||||
Or for Flatpak users:
|
Or for Flatpak users:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cp mirage/src/themes/Midnight.qpl \
|
cp mirage/src/themes/Foliage.qpl \
|
||||||
~/.var/app/io.github.mirukana.mirage/data/mirage/themes/MyTheme.qpl
|
~/.var/app/io.github.mirukana.mirage/data/mirage/themes/MyTheme.qpl
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
BIN
docs/screenshots/m01-chat.png
Normal file
After Width: | Height: | Size: 359 KiB |
BIN
docs/screenshots/m02-sign-in.png
Normal file
After Width: | Height: | Size: 390 KiB |
BIN
docs/screenshots/m03-account-settings.png
Normal file
After Width: | Height: | Size: 388 KiB |
BIN
docs/screenshots/m04-create-room.png
Normal file
After Width: | Height: | Size: 374 KiB |
BIN
docs/screenshots/m05-main-pane-small.png
Normal file
After Width: | Height: | Size: 84 KiB |
BIN
docs/screenshots/m06-chat-small.png
Normal file
After Width: | Height: | Size: 94 KiB |
BIN
docs/screenshots/m07-room-pane-small.png
Normal file
After Width: | Height: | Size: 80 KiB |
|
@ -27,7 +27,7 @@ QRC_FILE = $$BUILD_DIR/resources.qrc
|
||||||
RESOURCES += $$QRC_FILE
|
RESOURCES += $$QRC_FILE
|
||||||
HEADERS += $$glob_filenames(*.h) submodules/hsluv-c/src/hsluv.h
|
HEADERS += $$glob_filenames(*.h) submodules/hsluv-c/src/hsluv.h
|
||||||
SOURCES += $$glob_filenames(*.cpp) submodules/hsluv-c/src/hsluv.c
|
SOURCES += $$glob_filenames(*.cpp) submodules/hsluv-c/src/hsluv.c
|
||||||
TARGET = mirage
|
TARGET = moment
|
||||||
|
|
||||||
unix:!macx {
|
unix:!macx {
|
||||||
LIBS += -lX11 -lXss
|
LIBS += -lX11 -lXss
|
||||||
|
@ -61,12 +61,12 @@ no-x11 {
|
||||||
executables.files = $$TARGET
|
executables.files = $$TARGET
|
||||||
|
|
||||||
shortcuts.path = $$PREFIX/share/applications
|
shortcuts.path = $$PREFIX/share/applications
|
||||||
shortcuts.files = packaging/mirage.desktop
|
shortcuts.files = packaging/moment.desktop
|
||||||
|
|
||||||
icons256.path = $$PREFIX/share/icons/hicolor/256x256/apps
|
icons256.path = $$PREFIX/share/icons/hicolor/256x256/apps
|
||||||
icons256.files = packaging/mirage.png
|
icons256.files = packaging/moment.png
|
||||||
|
|
||||||
examples.path = $$PREFIX/share/examples/mirage
|
examples.path = $$PREFIX/share/examples/moment
|
||||||
examples.files = src/config/settings.py
|
examples.files = src/config/settings.py
|
||||||
|
|
||||||
INSTALLS += executables shortcuts icons256 examples
|
INSTALLS += executables shortcuts icons256 examples
|
||||||
|
@ -101,7 +101,7 @@ for(file, $$list($$glob_filenames(*.py))) {
|
||||||
}
|
}
|
||||||
|
|
||||||
QMAKE_CLEAN *= $$MOC_DIR $$OBJECTS_DIR $$RCC_DIR $$PYCACHE_DIRS $$QRC_FILE
|
QMAKE_CLEAN *= $$MOC_DIR $$OBJECTS_DIR $$RCC_DIR $$PYCACHE_DIRS $$QRC_FILE
|
||||||
QMAKE_CLEAN *= $$BUILD_DIR $$TARGET Makefile mirage.pro.user .qmake.stash
|
QMAKE_CLEAN *= $$BUILD_DIR $$TARGET Makefile moment.pro.user .qmake.stash
|
||||||
QMAKE_CLEAN *= $$glob_filenames(*.pyc, *.qmlc, *.jsc, *.egg-info)
|
QMAKE_CLEAN *= $$glob_filenames(*.pyc, *.qmlc, *.jsc, *.egg-info)
|
||||||
QMAKE_CLEAN *= packaging/flatpak/flatpak-env
|
QMAKE_CLEAN *= packaging/flatpak/flatpak-env
|
||||||
QMAKE_CLEAN *= packaging/flatpak/flatpak-pip-generator
|
QMAKE_CLEAN *= packaging/flatpak/flatpak-pip-generator
|
|
@ -1,16 +0,0 @@
|
||||||
#!/usr/bin/env sh
|
|
||||||
set -e
|
|
||||||
|
|
||||||
here="$(dirname "$(readlink -f "$0")")"
|
|
||||||
|
|
||||||
export RESTORE_LD_LIBRARY_PATH="$LD_LIBRARY_PATH"
|
|
||||||
export RESTORE_PYTHONHOME="$PYTHONHOME"
|
|
||||||
export RESTORE_PYTHONUSERBASE="$PYTHONUSERBASE"
|
|
||||||
|
|
||||||
export SSL_CERT_FILE="$here/usr/lib/python$PY_XY/site-packages/certifi/cacert.pem"
|
|
||||||
export LD_LIBRARY_PATH="$here/usr/lib:$LD_LIBRARY_PATH"
|
|
||||||
export PYTHONHOME="$here/usr"
|
|
||||||
export PYTHONUSERBASE="$here/usr"
|
|
||||||
|
|
||||||
cd "$here"
|
|
||||||
exec "$here/usr/bin/mirage" "$@"
|
|
|
@ -1,32 +0,0 @@
|
||||||
# AppImage building
|
|
||||||
|
|
||||||
The image must be built on Ubuntu 16.04 Xenial, to ensure compatibility with
|
|
||||||
older systems.
|
|
||||||
|
|
||||||
LXD can be used to setup a suitable container from any distro.
|
|
||||||
|
|
||||||
If not done already (all default settings are usually fine):
|
|
||||||
|
|
||||||
lxd init
|
|
||||||
|
|
||||||
Initialize a new container named `ubuntu`:
|
|
||||||
|
|
||||||
lxc launch images:ubuntu/xenial/amd64 ubuntu
|
|
||||||
|
|
||||||
Now, you can either clone the repo from inside the container...:
|
|
||||||
|
|
||||||
lxc exec ubuntu -- apt install -y git
|
|
||||||
lxc exec ubuntu -- git pull https://github.com/mirukana/mirage
|
|
||||||
|
|
||||||
...or directly copy a repository from your local filesystem inside:
|
|
||||||
|
|
||||||
lxc exec ubuntu -- /bin/mkdir -p /root/mirage
|
|
||||||
lxc file push -vr <path to repo root>/* ubuntu/root/mirage
|
|
||||||
|
|
||||||
Run the build script inside the container:
|
|
||||||
|
|
||||||
lxc exec ubuntu -- /root/mirage/packaging/appimage/build.sh
|
|
||||||
|
|
||||||
You can also start a shell inside (e.g. if something goes wrong):
|
|
||||||
|
|
||||||
lxc exec ubuntu -- /bin/bash
|
|
|
@ -1,252 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
set -eo pipefail
|
|
||||||
|
|
||||||
HERE="$(dirname "$(readlink -f "$0")")"
|
|
||||||
MIRAGE_REPO_URL='https://github.com/mirukana/mirage'
|
|
||||||
PY_XYZ=3.9.6
|
|
||||||
PY_XY="$(cut -d . -f 1-2 <<< "$PY_XYZ")"
|
|
||||||
|
|
||||||
|
|
||||||
check_distro() {
|
|
||||||
if grep -q '^\s*Ubuntu\s*16.04' /etc/issue; then return; fi
|
|
||||||
|
|
||||||
echo "Not running on expected distribution or version, aborting!" >&2
|
|
||||||
echo "See <repo root>/packaging/appimage/README.md for more info." >&2
|
|
||||||
exit 99
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
parse_cli_arguments() {
|
|
||||||
if [ "$1" = --skip-install-prerequisites ] || [ "$1" = -s ]; then
|
|
||||||
skip_pre=true
|
|
||||||
else
|
|
||||||
skip_pre=false
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
setup_dns() {
|
|
||||||
if ! grep -q 'dns-nameservers 9.9.9.9' /etc/network/interfaces; then
|
|
||||||
sed -i '/iface eth0 inet dhcp/a dns-nameservers 9.9.9.9' \
|
|
||||||
/etc/network/interfaces
|
|
||||||
|
|
||||||
invoke-rc.d networking restart
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
install_apt_packages() {
|
|
||||||
apt install -y software-properties-common
|
|
||||||
add-apt-repository -y ppa:beineri/opt-qt-5.12.9-xenial
|
|
||||||
add-apt-repository -y ppa:beineri/opt-qt-5.12.9-xenial
|
|
||||||
apt update -y
|
|
||||||
|
|
||||||
apt install -y \
|
|
||||||
qt512base qt512declarative qt512graphicaleffects \
|
|
||||||
qt512imageformats qt512quickcontrols2 qt512svg \
|
|
||||||
zip git wget cmake ccache \
|
|
||||||
build-essential mesa-common-dev libglu1-mesa-dev freeglut3-dev \
|
|
||||||
libglfw3-dev libgles2-mesa-dev libssl-dev \
|
|
||||||
python3-dev python3-setuptools python3-pip libgdbm-dev libc6-dev \
|
|
||||||
libsqlite3-dev libffi-dev openssl libreadline-dev \
|
|
||||||
libjpeg-turbo8-dev zlib1g-dev \
|
|
||||||
libtiff5-dev liblcms2-dev libwebp-dev libopenjp2-7-dev \
|
|
||||||
libx11-dev libxss-dev libasound2-dev \
|
|
||||||
pkg-config libdbus-1-dev libglib2.0-dev \
|
|
||||||
appstream-util desktop-file-utils # for appimage-lint.sh
|
|
||||||
|
|
||||||
/usr/sbin/update-ccache-symlinks
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
setup_env() {
|
|
||||||
set +euo pipefail
|
|
||||||
# shellcheck disable=SC1091
|
|
||||||
source /opt/qt512/bin/qt512-env.sh
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
export PATH="/usr/lib/ccache:$PATH"
|
|
||||||
export LD_LIBRARY_PATH="$HOME/.local/lib/python$PY_XY/site-packages/PIL/.libs/:$HOME/.local/lib/python$PY_XY/site-packages/.libs_cffi_backend/:/usr/lib/x86_64-linux-gnu/:/usr/lib:$LD_LIBRARY_PATH"
|
|
||||||
export PREFIX=/usr
|
|
||||||
|
|
||||||
export CFLAGS="-march=x86-64 -O2 -pipe -fPIC"
|
|
||||||
export CXXFLAGS="$CFLAGS"
|
|
||||||
export MAKEFLAGS="-j$(($(nproc) + 1))"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
install_python() {
|
|
||||||
cd ~
|
|
||||||
|
|
||||||
if ! [ -d ~/.pyenv ]; then
|
|
||||||
wget -O - https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash
|
|
||||||
fi
|
|
||||||
|
|
||||||
export PYENV_ROOT="$HOME/.pyenv"
|
|
||||||
export PATH="$PYENV_ROOT/bin:$PATH"
|
|
||||||
|
|
||||||
set +euo pipefail
|
|
||||||
eval "$(pyenv init --path)"
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
export PYTHON_CFLAGS="$CFLAGS"
|
|
||||||
export PYTHON_CONFIGURE_OPTS='--enable-shared --enable-optimizations --with-lto'
|
|
||||||
|
|
||||||
pyenv update
|
|
||||||
pyenv install --verbose --skip-existing $PY_XYZ
|
|
||||||
pyenv global $PY_XYZ
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
install_olm() {
|
|
||||||
cd ~
|
|
||||||
|
|
||||||
rm -rf olm-master.tar.gz olm-master
|
|
||||||
wget 'https://gitlab.matrix.org/matrix-org/olm/-/archive/master/olm-master.tar.gz'
|
|
||||||
tar xf olm-master.tar.gz
|
|
||||||
|
|
||||||
cd olm-master
|
|
||||||
make clean
|
|
||||||
cmake . -Bbuild
|
|
||||||
cmake --build build
|
|
||||||
make install
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
install_pyotherside() {
|
|
||||||
cd ~
|
|
||||||
|
|
||||||
if ! [ -f 1.5.9.tar.gz ]; then
|
|
||||||
wget 'https://github.com/thp/pyotherside/archive/1.5.9.tar.gz'
|
|
||||||
fi
|
|
||||||
|
|
||||||
tar xf 1.5.9.tar.gz
|
|
||||||
|
|
||||||
cd pyotherside-1.5.9
|
|
||||||
make clean
|
|
||||||
find . -name Makefile -delete
|
|
||||||
qmake
|
|
||||||
make install
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
get_app_and_pip_dependencies() {
|
|
||||||
cd ~
|
|
||||||
|
|
||||||
if ! [ -d mirage ]; then
|
|
||||||
git clone --recursive "$MIRAGE_REPO_URL"
|
|
||||||
fi
|
|
||||||
|
|
||||||
cd mirage
|
|
||||||
if pip3 show Pillow; then pip3 uninstall Pillow --yes; fi
|
|
||||||
pip3 install Pillow --no-binary :all:
|
|
||||||
pip3 install --user -Ur requirements.txt
|
|
||||||
pip3 install --user -U certifi
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
initialize_appdir() {
|
|
||||||
cd ~/mirage
|
|
||||||
rm -rf .qmake.stash Makefile build
|
|
||||||
|
|
||||||
qmake mirage.pro PREFIX=/usr
|
|
||||||
make install INSTALL_ROOT=build/appdir
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
complete_appdir() {
|
|
||||||
cd ~/mirage/build
|
|
||||||
|
|
||||||
cp -r ~/.pyenv/versions/$PY_XYZ/* appdir/usr
|
|
||||||
cp -r "$HOME/.local/lib/python$PY_XY/site-packages/"* \
|
|
||||||
"appdir/usr/lib/python$PY_XY/site-packages"
|
|
||||||
|
|
||||||
cd ~/mirage/build/appdir/usr/lib
|
|
||||||
ln -s "python$PY_XY/site-packages/Pillow.libs/"* .
|
|
||||||
cd ~/mirage/build
|
|
||||||
|
|
||||||
if ! [ -f ~/linuxdeployqt.AppImage ]; then
|
|
||||||
wget 'https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage' \
|
|
||||||
-O ~/linuxdeployqt.AppImage
|
|
||||||
fi
|
|
||||||
chmod +x ~/linuxdeployqt.AppImage
|
|
||||||
|
|
||||||
~/linuxdeployqt.AppImage appdir/usr/share/applications/mirage.desktop \
|
|
||||||
-bundle-non-qt-libs -qmldir=../src/gui
|
|
||||||
|
|
||||||
mkdir -p appdir/usr/share/metainfo
|
|
||||||
cp ~/mirage/packaging/mirage.appdata.xml appdir/usr/share/metainfo
|
|
||||||
|
|
||||||
cp /opt/qt512/qml/io/thp/pyotherside/qmldir \
|
|
||||||
appdir/usr/qml/io/thp/pyotherside
|
|
||||||
|
|
||||||
# Remove useless heavy test data
|
|
||||||
rm -rf "appdir/usr/lib/python$PY_XY/test"
|
|
||||||
rm -rf "appdir/usr/lib/python$PY_XY/site-packages/Crypto/SelfTest/"
|
|
||||||
|
|
||||||
# Remove python cache files
|
|
||||||
find appdir -name '*.pyc' -delete
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fix_apprun_launcher() {
|
|
||||||
cd ~/mirage/build/appdir
|
|
||||||
rm -f AppRun # because it's a symlink
|
|
||||||
sed "s/\\\$PY_XY/$PY_XY/" "$HERE/AppRun.sh" > AppRun
|
|
||||||
chmod +x AppRun
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
generate_appimage() {
|
|
||||||
cd ~/mirage/build
|
|
||||||
|
|
||||||
if ! [ -f ~/appimagetool.AppImage ]; then
|
|
||||||
wget "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage" \
|
|
||||||
-O ~/appimagetool.AppImage
|
|
||||||
fi
|
|
||||||
|
|
||||||
chmod +x ~/appimagetool.AppImage
|
|
||||||
~/appimagetool.AppImage --no-appstream appdir
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
lint_appdir() {
|
|
||||||
cd ~
|
|
||||||
|
|
||||||
cat << 'EOF' > /usr/local/bin/mimetype
|
|
||||||
#!/usr/bin/env sh
|
|
||||||
file --mime-type "$@" | tr -d ';'
|
|
||||||
EOF
|
|
||||||
chmod +x /usr/local/bin/mimetype
|
|
||||||
|
|
||||||
if ! [ -d pkg2appimage ]; then
|
|
||||||
git clone https://github.com/AppImage/pkg2appimage
|
|
||||||
fi
|
|
||||||
chmod +x pkg2appimage/appdir-lint.sh
|
|
||||||
|
|
||||||
cd ~/mirage/build
|
|
||||||
echo -e "\e[34m\nAppDir linting result:\n\e[0m"
|
|
||||||
~/pkg2appimage/appdir-lint.sh appdir
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
check_distro
|
|
||||||
parse_cli_arguments "$@"
|
|
||||||
setup_dns
|
|
||||||
|
|
||||||
if [ "$skip_pre" = false ]; then install_apt_packages; fi
|
|
||||||
|
|
||||||
setup_env
|
|
||||||
|
|
||||||
if [ "$skip_pre" = false ]; then
|
|
||||||
install_python
|
|
||||||
install_olm
|
|
||||||
install_pyotherside
|
|
||||||
get_app_and_pip_dependencies
|
|
||||||
fi
|
|
||||||
|
|
||||||
initialize_appdir
|
|
||||||
complete_appdir
|
|
||||||
fix_apprun_launcher
|
|
||||||
generate_appimage
|
|
||||||
lint_appdir
|
|
|
@ -19,4 +19,4 @@ async_generator >= 1.10, < 2; python_version < "3.7"
|
||||||
dataclasses >= 0.6, < 0.7; python_version < "3.7"
|
dataclasses >= 0.6, < 0.7; python_version < "3.7"
|
||||||
pyfastcopy >= 1.0.3, < 2; python_version < "3.8"
|
pyfastcopy >= 1.0.3, < 2; python_version < "3.8"
|
||||||
|
|
||||||
git+https://github.com/mirukana/matrix-nio#egg-matrix-nio[e2e]
|
git+https://github.com/MRAAGH/matrix-nio#egg-matrix-nio[e2e]
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
# Copyright Mirage authors & contributors <https://github.com/mirukana/mirage>
|
# Copyright Mirage authors & contributors <https://github.com/mirukana/mirage>
|
||||||
|
# and Moment contributors <https://gitlab.com/mx-moment/moment>
|
||||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
|
||||||
"""This package provides Mirage's backend side that can interact with the UI.
|
"""This package provides Moment's backend side that can interact with the UI.
|
||||||
|
|
||||||
To learn more about how this package works, you might want to check the
|
To learn more about how this package works, you might want to check the
|
||||||
documentation in the following modules first:
|
documentation in the following modules first:
|
||||||
|
@ -12,7 +13,7 @@ documentation in the following modules first:
|
||||||
- `nio_callbacks`
|
- `nio_callbacks`
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__app_name__ = "mirage"
|
__app_name__ = "moment"
|
||||||
__display_name__ = "Mirage"
|
__display_name__ = "Moment"
|
||||||
__reverse_dns__ = "io.github.mirukana.mirage"
|
__reverse_dns__ = "xyz.mx-moment"
|
||||||
__version__ = "0.7.2"
|
__version__ = "0.7.3"
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# Copyright Mirage authors & contributors <https://github.com/mirukana/mirage>
|
# Copyright Mirage authors & contributors <https://github.com/mirukana/mirage>
|
||||||
|
# and Moment contributors <https://gitlab.com/mx-moment/moment>
|
||||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
@ -104,7 +105,7 @@ class Backend:
|
||||||
media_cache: A matrix media cache for downloaded files.
|
media_cache: A matrix media cache for downloaded files.
|
||||||
|
|
||||||
presences: A `{user_id: Presence}` dict for storing presence info about
|
presences: A `{user_id: Presence}` dict for storing presence info about
|
||||||
matrix users registered on Mirage.
|
matrix users registered on Moment.
|
||||||
|
|
||||||
mxc_events: A dict storing media `Event` model items for any account
|
mxc_events: A dict storing media `Event` model items for any account
|
||||||
that have the same mxc URI
|
that have the same mxc URI
|
||||||
|
@ -137,7 +138,7 @@ class Backend:
|
||||||
DefaultDict(asyncio.Lock) # {room_id: lock}
|
DefaultDict(asyncio.Lock) # {room_id: lock}
|
||||||
|
|
||||||
cache_dir = Path(
|
cache_dir = Path(
|
||||||
os.environ.get("MIRAGE_CACHE_DIR") or self.appdirs.user_cache_dir,
|
os.environ.get("MOMENT_CACHE_DIR") or self.appdirs.user_cache_dir,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.media_cache: MediaCache = MediaCache(self, cache_dir)
|
self.media_cache: MediaCache = MediaCache(self, cache_dir)
|
||||||
|
@ -552,7 +553,13 @@ class Backend:
|
||||||
)
|
)
|
||||||
|
|
||||||
api_list = "https://publiclist.anchel.nl/publiclist.json"
|
api_list = "https://publiclist.anchel.nl/publiclist.json"
|
||||||
|
try:
|
||||||
response = await session.get(api_list)
|
response = await session.get(api_list)
|
||||||
|
except:
|
||||||
|
await session.close()
|
||||||
|
print("Unable to fetch", api_list)
|
||||||
|
return
|
||||||
|
|
||||||
coros = []
|
coros = []
|
||||||
|
|
||||||
for server in (await response.json()):
|
for server in (await response.json()):
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# Copyright Mirage authors & contributors <https://github.com/mirukana/mirage>
|
# Copyright Mirage authors & contributors <https://github.com/mirukana/mirage>
|
||||||
|
# and Moment contributors <https://gitlab.com/mx-moment/moment>
|
||||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
|
||||||
"""Matrix media downloading, caching and retrieval."""
|
"""Matrix media downloading, caching and retrieval."""
|
||||||
|
@ -95,7 +96,7 @@ class Media:
|
||||||
<base download folder>/<homeserver domain>/
|
<base download folder>/<homeserver domain>/
|
||||||
<file title>_<mxc id>.<file extension>`
|
<file title>_<mxc id>.<file extension>`
|
||||||
```
|
```
|
||||||
e.g. `~/.cache/mirage/downloads/matrix.org/foo_Hm24ar11i768b0el.png`.
|
e.g. `~/.cache/moment/downloads/matrix.org/foo_Hm24ar11i768b0el.png`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
parsed = urlparse(self.mxc)
|
parsed = urlparse(self.mxc)
|
||||||
|
@ -304,7 +305,7 @@ class Thumbnail(Media):
|
||||||
<file title>_<mxc id>.<file extension>`
|
<file title>_<mxc id>.<file extension>`
|
||||||
```
|
```
|
||||||
e.g.
|
e.g.
|
||||||
`~/.cache/mirage/thumbnails/matrix.org/32x32/foo_Hm24ar11i768b0el.png`.
|
`~/.cache/moment/thumbnails/matrix.org/32x32/foo_Hm24ar11i768b0el.png`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
size = self.normalize_size(self.server_size or self.wanted_size)
|
size = self.normalize_size(self.server_size or self.wanted_size)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# Copyright Mirage authors & contributors <https://github.com/mirukana/mirage>
|
# Copyright Mirage authors & contributors <https://github.com/mirukana/mirage>
|
||||||
|
# and Moment contributors <https://gitlab.com/mx-moment/moment>
|
||||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
|
||||||
"""User data and configuration files definitions."""
|
"""User data and configuration files definitions."""
|
||||||
|
@ -197,7 +198,7 @@ class ConfigFile(UserFile):
|
||||||
@property
|
@property
|
||||||
def path(self) -> Path:
|
def path(self) -> Path:
|
||||||
return Path(
|
return Path(
|
||||||
os.environ.get("MIRAGE_CONFIG_DIR") or
|
os.environ.get("MOMENT_CONFIG_DIR") or
|
||||||
self.backend.appdirs.user_config_dir,
|
self.backend.appdirs.user_config_dir,
|
||||||
) / self.filename
|
) / self.filename
|
||||||
|
|
||||||
|
@ -209,7 +210,7 @@ class UserDataFile(UserFile):
|
||||||
@property
|
@property
|
||||||
def path(self) -> Path:
|
def path(self) -> Path:
|
||||||
return Path(
|
return Path(
|
||||||
os.environ.get("MIRAGE_DATA_DIR") or
|
os.environ.get("MOMENT_DATA_DIR") or
|
||||||
self.backend.appdirs.user_data_dir,
|
self.backend.appdirs.user_data_dir,
|
||||||
) / self.filename
|
) / self.filename
|
||||||
|
|
||||||
|
@ -460,7 +461,7 @@ class NewTheme(UserDataFile, PCNFile):
|
||||||
@property
|
@property
|
||||||
def path(self) -> Path:
|
def path(self) -> Path:
|
||||||
data_dir = Path(
|
data_dir = Path(
|
||||||
os.environ.get("MIRAGE_DATA_DIR") or
|
os.environ.get("MOMENT_DATA_DIR") or
|
||||||
self.backend.appdirs.user_data_dir,
|
self.backend.appdirs.user_data_dir,
|
||||||
)
|
)
|
||||||
return data_dir / "themes" / self.filename
|
return data_dir / "themes" / self.filename
|
||||||
|
@ -515,17 +516,17 @@ class Theme(UserDataFile):
|
||||||
@property
|
@property
|
||||||
def path(self) -> Path:
|
def path(self) -> Path:
|
||||||
data_dir = Path(
|
data_dir = Path(
|
||||||
os.environ.get("MIRAGE_DATA_DIR") or
|
os.environ.get("MOMENT_DATA_DIR") or
|
||||||
self.backend.appdirs.user_data_dir,
|
self.backend.appdirs.user_data_dir,
|
||||||
)
|
)
|
||||||
return data_dir / "themes" / self.filename
|
return data_dir / "themes" / self.filename
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def default_data(self) -> str:
|
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}"
|
path = f"src/themes/{self.filename}"
|
||||||
else:
|
else:
|
||||||
path = "src/themes/Midnight.qpl"
|
path = "src/themes/Foliage.qpl"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
byte_content = pyotherside.qrc_get_file_contents(path)
|
byte_content = pyotherside.qrc_get_file_contents(path)
|
||||||
|
|
|
@ -26,13 +26,14 @@ class General:
|
||||||
tooltips_delay: float = 0.7
|
tooltips_delay: float = 0.7
|
||||||
|
|
||||||
# Application theme to use.
|
# Application theme to use.
|
||||||
# Can be the name of a built-in theme (Mirage.qpl or Glass.qpl), or
|
# Can be the name of a built-in theme (Foliage.qpl, Midnight.qpl or
|
||||||
# the name (including extension) of a file in the user theme folder, which
|
# Glass.qpl), or the name (including extension) of a file in the user theme
|
||||||
# is "$XDG_DATA_HOME/mirage/themes" if that environment variable is set,
|
# folder, which is "$XDG_DATA_HOME/moment/themes" if that environment
|
||||||
# else "~/.local/share/mirage/themes".
|
# variable is set,
|
||||||
|
# else "~/.local/share/moment/themes".
|
||||||
# For Flatpak, it is
|
# For Flatpak, it is
|
||||||
# "~/.var/app/io.github.mirukana.mirage/data/mirage/themes".
|
# "~/.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.
|
# Interface scale multiplier, e.g. 0.5 makes everything half-size.
|
||||||
zoom: float = 1.0
|
zoom: float = 1.0
|
||||||
|
@ -204,7 +205,7 @@ class Chat:
|
||||||
auto_play_gif: bool = True
|
auto_play_gif: bool = True
|
||||||
|
|
||||||
# When clicking on a file in the timeline, open it in an external
|
# When clicking on a file in the timeline, open it in an external
|
||||||
# program instead of displaying it using Mirage's interface.
|
# program instead of displaying it using Moment's interface.
|
||||||
# On Linux, the xdg-open command is called.
|
# On Linux, the xdg-open command is called.
|
||||||
click_opens_externally: bool = False
|
click_opens_externally: bool = False
|
||||||
|
|
||||||
|
@ -234,7 +235,7 @@ class Keys:
|
||||||
# would need to be pressed.
|
# would need to be pressed.
|
||||||
#
|
#
|
||||||
# A list of default bindings can be found at:
|
# A list of default bindings can be found at:
|
||||||
# https://github.com/mirukana/mirage/blob/master/docs/KEYBINDINGS.md
|
# https://gitlab.com/mx-moment/moment/-/blob/main/docs/KEYBINDINGS.md
|
||||||
|
|
||||||
# Helper functions
|
# Helper functions
|
||||||
|
|
||||||
|
@ -280,7 +281,7 @@ class Keys:
|
||||||
qml_console = ["F1"]
|
qml_console = ["F1"]
|
||||||
|
|
||||||
# Start the Python backend debugger.
|
# Start the Python backend debugger.
|
||||||
# Mirage must be connected to a terminal for this to work.
|
# Moment must be connected to a terminal for this to work.
|
||||||
python_debugger = ["Shift+F1"]
|
python_debugger = ["Shift+F1"]
|
||||||
|
|
||||||
# Start the Python backend debugger in remote access mode.
|
# Start the Python backend debugger in remote access mode.
|
||||||
|
@ -288,8 +289,8 @@ class Keys:
|
||||||
# From any terminal, run `socat readline tcp:127.0.0.1:4444` to connect.
|
# From any terminal, run `socat readline tcp:127.0.0.1:4444` to connect.
|
||||||
python_remote_debugger = ["Alt+F1"]
|
python_remote_debugger = ["Alt+F1"]
|
||||||
|
|
||||||
# Quit Mirage
|
# Quit Moment
|
||||||
quit = []
|
quit = ["Ctrl+Q"]
|
||||||
|
|
||||||
class Scrolling: # Keys.Scrolling
|
class Scrolling: # Keys.Scrolling
|
||||||
# Pages and chat timeline scrolling
|
# Pages and chat timeline scrolling
|
||||||
|
@ -349,7 +350,7 @@ class Keys:
|
||||||
# When focusing the field, use Tab/Shift+Tab or the arrows to navigate
|
# When focusing the field, use Tab/Shift+Tab or the arrows to navigate
|
||||||
# the list, Enter to switch to focused account/room, Escape to cancel,
|
# the list, Enter to switch to focused account/room, Escape to cancel,
|
||||||
# Menu to open the context menu.
|
# Menu to open the context menu.
|
||||||
focus_filter = ["Alt+F"]
|
focus_filter = ["Alt+F","Ctrl+K"]
|
||||||
clear_filter = ["Alt+Shift+F"]
|
clear_filter = ["Alt+Shift+F"]
|
||||||
|
|
||||||
# Switch to the previous/next room in the list.
|
# Switch to the previous/next room in the list.
|
||||||
|
@ -431,7 +432,7 @@ class Keys:
|
||||||
# Focus the previous/next message in the timeline.
|
# Focus the previous/next message in the timeline.
|
||||||
# Keybinds defined below in this section affect the focused message.
|
# Keybinds defined below in this section affect the focused message.
|
||||||
# The Menu key can open the context menu for a focused message.
|
# The Menu key can open the context menu for a focused message.
|
||||||
previous = ["Ctrl+Up", "Ctrl+K"]
|
previous = ["Ctrl+Up", "Ctrl+I"]
|
||||||
next = ["Ctrl+Down", "Ctrl+J"]
|
next = ["Ctrl+Down", "Ctrl+J"]
|
||||||
|
|
||||||
# Select the currently focused message, same as clicking on it.
|
# Select the currently focused message, same as clicking on it.
|
||||||
|
@ -461,12 +462,12 @@ class Keys:
|
||||||
|
|
||||||
# Remove the selected messages if any, else the focused message if any,
|
# Remove the selected messages if any, else the focused message if any,
|
||||||
# else the last message you posted.
|
# else the last message you posted.
|
||||||
remove = ["Ctrl+R", "Alt+Del"]
|
remove = ["Ctrl+Shift+R", "Alt+Del"]
|
||||||
|
|
||||||
# Reply/cancel reply to the focused message if any,
|
# Reply/cancel reply to the focused message if any,
|
||||||
# else the last message posted by someone else.
|
# else the last message posted by someone else.
|
||||||
# Replying can also be cancelled by pressing Escape.
|
# Replying can also be cancelled by pressing Escape.
|
||||||
reply = ["Ctrl+Q"]
|
reply = ["Ctrl+R"]
|
||||||
|
|
||||||
# Open the QML developer console for the focused message if any,
|
# Open the QML developer console for the focused message if any,
|
||||||
# and display the event source.
|
# and display the event source.
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
// Copyright Mirage authors & contributors <https://github.com/mirukana/mirage>
|
// Copyright Mirage authors & contributors <https://github.com/mirukana/mirage>
|
||||||
|
// and Moment contributors <https://gitlab.com/mx-moment/moment>
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
|
||||||
pragma Singleton
|
pragma Singleton
|
||||||
|
@ -18,9 +19,9 @@ QtObject {
|
||||||
-h, --help Show this help and exit
|
-h, --help Show this help and exit
|
||||||
|
|
||||||
Environment variables:
|
Environment variables:
|
||||||
MIRAGE_CONFIG_DIR Override the default configuration folder
|
MOMENT_CONFIG_DIR Override the default configuration folder
|
||||||
MIRAGE_DATA_DIR Override the default application data folder
|
MOMENT_DATA_DIR Override the default application data folder
|
||||||
MIRAGE_CACHE_DIR Override the default cache and downloads folder
|
MOMENT_CACHE_DIR Override the default cache and downloads folder
|
||||||
http_proxy Override the General.proxy setting, see settings.py
|
http_proxy Override the General.proxy setting, see settings.py
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,7 @@ Rectangle {
|
||||||
icon.name: "documentation"
|
icon.name: "documentation"
|
||||||
text: qsTr("Online documentation")
|
text: qsTr("Online documentation")
|
||||||
onTriggered: Qt.openUrlExternally(
|
onTriggered: Qt.openUrlExternally(
|
||||||
"https://github.com/mirukana/mirage/tree/master/docs"
|
"https://gitlab.com/mx-moment/moment/-/tree/main/docs"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -129,7 +129,7 @@ HListView {
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: autoSaveTimer
|
id: autoSaveTimer
|
||||||
interval: 30000
|
interval: 3000
|
||||||
onTriggered: root.save()
|
onTriggered: root.save()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -246,7 +246,7 @@ HBox {
|
||||||
Timer {
|
Timer {
|
||||||
interval: 1000
|
interval: 1000
|
||||||
running:
|
running:
|
||||||
fetchServersFutureId === "" &&
|
// fetchServersFutureId === "" &&
|
||||||
ModelStore.get("homeservers").count === 0
|
ModelStore.get("homeservers").count === 0
|
||||||
|
|
||||||
repeat: true
|
repeat: true
|
||||||
|
|
|
@ -11,7 +11,7 @@ HFlickableColumnPopup {
|
||||||
property string path
|
property string path
|
||||||
|
|
||||||
readonly property string docs:
|
readonly property string docs:
|
||||||
"https://github.com/mirukana/mirage/tree/master/docs"
|
"https://gitlab.com/mx-moment/moment/-/tree/main/docs"
|
||||||
|
|
||||||
page.footer: AutoDirectionLayout {
|
page.footer: AutoDirectionLayout {
|
||||||
CancelButton {
|
CancelButton {
|
||||||
|
|
|
@ -27,7 +27,7 @@ HColumnPopup {
|
||||||
text: qsTr("Report")
|
text: qsTr("Report")
|
||||||
icon.name: "report-error"
|
icon.name: "report-error"
|
||||||
onClicked: Qt.openUrlExternally(
|
onClicked: Qt.openUrlExternally(
|
||||||
"https://github.com/mirukana/mirage/blob/master/docs/" +
|
"https://gitlab.com/mx-moment/moment/-/blob/main/docs/" +
|
||||||
"CONTRIBUTING.md#issues"
|
"CONTRIBUTING.md#issues"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
BIN
src/images/foliage.jpg
Normal file
After Width: | Height: | Size: 246 KiB |
250
src/main.cpp
|
@ -1,4 +1,5 @@
|
||||||
// Copyright Mirage authors & contributors <https://github.com/mirukana/mirage>
|
// Copyright Mirage authors & contributors <https://github.com/mirukana/mirage>
|
||||||
|
// and Moment contributors <https://gitlab.com/mx-moment/moment>
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
|
||||||
// This file creates the application, registers custom objects for QML
|
// This file creates the application, registers custom objects for QML
|
||||||
|
@ -17,6 +18,7 @@
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QLockFile>
|
#include <QLockFile>
|
||||||
|
#include <QMessageBox>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
|
||||||
#ifdef Q_OS_UNIX
|
#ifdef Q_OS_UNIX
|
||||||
|
@ -97,6 +99,236 @@ void onExitSignal(int signum) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void migrateFile(QDir source, QDir destination, QString fname) {
|
||||||
|
if (! QFile::copy(source.filePath(fname), destination.filePath(fname)))
|
||||||
|
qWarning() << "Could not migrate" << fname;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void migrateShallowDirectory(
|
||||||
|
QDir sourceParent, QDir destinationParent, QString dname
|
||||||
|
) {
|
||||||
|
if (sourceParent.exists(dname)) {
|
||||||
|
if (! destinationParent.mkpath(dname)) {
|
||||||
|
qWarning() << "Could not create directory"
|
||||||
|
<< destinationParent.filePath(dname);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
QDir source(sourceParent.filePath(dname));
|
||||||
|
QDir destination(destinationParent.filePath(dname));
|
||||||
|
QFileInfoList files = source.entryInfoList();
|
||||||
|
for (QFileInfo file : files) {
|
||||||
|
if(file.fileName() == "." || file.fileName() == "..")
|
||||||
|
continue;
|
||||||
|
migrateFile(source, destination, file.fileName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void offerMigrateFromMirage(
|
||||||
|
QDir configDirMoment, QDir configDirMirage,
|
||||||
|
QDir dataDirMoment, QDir dataDirMirage
|
||||||
|
) {
|
||||||
|
QMessageBox dialog;
|
||||||
|
dialog.setText("Would you like Moment to re-use your logins, "
|
||||||
|
"encryption keys, configuration and themes from Mirage?");
|
||||||
|
dialog.setInformativeText(
|
||||||
|
"Will copy "+configDirMirage.path()+" → "+configDirMoment.path()
|
||||||
|
+"\nWill copy "+dataDirMirage.path()+" → "+dataDirMoment.path());
|
||||||
|
dialog.addButton(QMessageBox::Yes);
|
||||||
|
dialog.addButton(QMessageBox::No);
|
||||||
|
dialog.addButton(QMessageBox::Cancel);
|
||||||
|
int result = dialog.exec();
|
||||||
|
if (result == QMessageBox::Yes) {
|
||||||
|
qWarning("Migrating config and data from Mirage");
|
||||||
|
if (! configDirMoment.mkpath(".")) {
|
||||||
|
qFatal("Could not create config directory");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
if (! dataDirMoment.mkpath(".")) {
|
||||||
|
qFatal("Could not create data directory");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
migrateFile(configDirMirage, configDirMoment, "settings.py");
|
||||||
|
migrateFile(configDirMirage, configDirMoment, "settings.gui.json");
|
||||||
|
migrateFile(configDirMirage, configDirMoment, "accounts.json");
|
||||||
|
migrateFile(dataDirMirage, dataDirMoment, "history.json");
|
||||||
|
migrateFile(dataDirMirage, dataDirMoment, "state.json");
|
||||||
|
migrateShallowDirectory(dataDirMirage, dataDirMoment, "themes");
|
||||||
|
migrateShallowDirectory(dataDirMirage, dataDirMoment, "encryption");
|
||||||
|
} else if (result == QMessageBox::No) {
|
||||||
|
// Nothing to do. Proceed with starting the app
|
||||||
|
qWarning("Not migrating");
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
// Neither "Yes" nor "No" was chosen.
|
||||||
|
// We can't know what the user wants. Just quit.
|
||||||
|
qWarning("Quitting. You can decide about migration next time.");
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool shouldMigrateFromMirage() {
|
||||||
|
QString genericConfig = QStandardPaths::writableLocation(
|
||||||
|
QStandardPaths::GenericConfigLocation);
|
||||||
|
QString genericData = QStandardPaths::writableLocation(
|
||||||
|
QStandardPaths::GenericDataLocation);
|
||||||
|
|
||||||
|
// Check whether Moment config already exists
|
||||||
|
{
|
||||||
|
QString customConfigDirMoment(
|
||||||
|
qEnvironmentVariable("MOMENT_CONFIG_DIR")
|
||||||
|
);
|
||||||
|
if (! customConfigDirMoment.isEmpty()) {
|
||||||
|
// MOMENT_CONFIG_DIR is set.
|
||||||
|
// Moment would definitely use this as the config directory.
|
||||||
|
if (QDir(customConfigDirMoment).exists())
|
||||||
|
// But it already exists.
|
||||||
|
// So this is not the first time Moment was started.
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
// No MOMENT_CONFIG_DIR, so check the default config directory.
|
||||||
|
if (QDir(genericConfig + "/moment").exists())
|
||||||
|
// The default config folder exists.
|
||||||
|
// So this is not the first time Moment was started.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether Moment data already exists
|
||||||
|
{
|
||||||
|
QString customDataDirMoment(qEnvironmentVariable("MOMENT_DATA_DIR"));
|
||||||
|
if (! customDataDirMoment.isEmpty()) {
|
||||||
|
// MOMENT_DATA_DIR is set.
|
||||||
|
// Moment would definitely use this as the data directory.
|
||||||
|
if (QDir(customDataDirMoment).exists())
|
||||||
|
// But it already exists.
|
||||||
|
// So this is not the first time Moment was started.
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
// No MOMENT_DATA_DIR, so check the default data directory.
|
||||||
|
if (QDir(genericData + "/moment").exists())
|
||||||
|
// The default data folder exists.
|
||||||
|
// So this is not the first time Moment was started.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether Mirage config exists
|
||||||
|
{
|
||||||
|
QString customConfigDirMirage(
|
||||||
|
qEnvironmentVariable("MIRAGE_CONFIG_DIR")
|
||||||
|
);
|
||||||
|
if (! customConfigDirMirage.isEmpty()) {
|
||||||
|
// MIRAGE_CONFIG_DIR is set.
|
||||||
|
// Mirage would definitely use this as the config directory.
|
||||||
|
if (! QDir(customConfigDirMirage).exists())
|
||||||
|
// But this directory does not exist.
|
||||||
|
// So there is nowhere to migrate from.
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
// No MIRAGE_CONFIG_DIR, so check the default config directory.
|
||||||
|
// Check /matrix-mirage (Debian) first, since it is more specific
|
||||||
|
if (! QDir(genericConfig + "/matrix-mirage").exists())
|
||||||
|
// Default Debian config folder does not exist,
|
||||||
|
// so check whether the normal /mirage exists
|
||||||
|
if (! QDir(genericConfig + "/mirage").exists())
|
||||||
|
// No, neither /matrix-mirage nor /mirage exist.
|
||||||
|
// So there is nowhere to migrate from.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We found out that there is no Moment config dir nor Moment data dir,
|
||||||
|
// but there is a Mirage config dir which can be migrated from.
|
||||||
|
// We could also check for Mirage data dir but it doesn't really matter.
|
||||||
|
// User should definitely be prompted for migration.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void tryMigrateFromMirage() {
|
||||||
|
QString genericConfig = QStandardPaths::writableLocation(
|
||||||
|
QStandardPaths::GenericConfigLocation);
|
||||||
|
QString genericData = QStandardPaths::writableLocation(
|
||||||
|
QStandardPaths::GenericDataLocation);
|
||||||
|
|
||||||
|
QDir configDirMoment, configDirMirage, dataDirMoment, dataDirMirage;
|
||||||
|
|
||||||
|
QString customConfigDirMoment(qEnvironmentVariable("MOMENT_CONFIG_DIR"));
|
||||||
|
configDirMoment = QDir(customConfigDirMoment.isEmpty()
|
||||||
|
? genericConfig + "/moment" : customConfigDirMoment);
|
||||||
|
|
||||||
|
QString customDataDirMoment(qEnvironmentVariable("MOMENT_DATA_DIR"));
|
||||||
|
dataDirMoment = QDir(customDataDirMoment.isEmpty()
|
||||||
|
? genericData + "/moment" : customDataDirMoment);
|
||||||
|
|
||||||
|
QString customConfigDirMirage(qEnvironmentVariable("MIRAGE_CONFIG_DIR"));
|
||||||
|
if (! customConfigDirMirage.isEmpty()) {
|
||||||
|
// MIRAGE_CONFIG_DIR is set.
|
||||||
|
// Mirage would definitely use this as the config directory.
|
||||||
|
// So this is where we should migrate from.
|
||||||
|
configDirMirage = QDir(customConfigDirMirage);
|
||||||
|
} else {
|
||||||
|
// No MIRAGE_CONFIG_DIR.
|
||||||
|
// Check if Mirage default config directory exists
|
||||||
|
// Check /matrix-mirage (Debian) first
|
||||||
|
QDir dirDeb(genericConfig + "/matrix-mirage");
|
||||||
|
if (dirDeb.exists()) {
|
||||||
|
// Default Debian config dir exists.
|
||||||
|
// So this is where we should migrate from.
|
||||||
|
configDirMirage = dirDeb;
|
||||||
|
} else {
|
||||||
|
// No /matrix-mirage found, so check /mirage
|
||||||
|
QDir dir(genericConfig + "/mirage");
|
||||||
|
if (dir.exists())
|
||||||
|
// Default config dir exists.
|
||||||
|
// So this is where we should migrate from.
|
||||||
|
configDirMirage = dir;
|
||||||
|
else
|
||||||
|
// No Mirage config dir found.
|
||||||
|
// Do not migrate.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString customDataDirMirage(qEnvironmentVariable("MIRAGE_DATA_DIR"));
|
||||||
|
if (! customDataDirMirage.isEmpty()) {
|
||||||
|
// MIRAGE_DATA_DIR is set.
|
||||||
|
// Mirage would definitely use this as the data directory.
|
||||||
|
// So this is where we should migrate from.
|
||||||
|
dataDirMirage = QDir(customDataDirMirage);
|
||||||
|
} else {
|
||||||
|
// No MIRAGE_DATA_DIR.
|
||||||
|
// Check if Mirage default data directory exists
|
||||||
|
// Check /matrix-mirage (Debian) first
|
||||||
|
QDir dirDeb(genericData + "/matrix-mirage");
|
||||||
|
if (dirDeb.exists()) {
|
||||||
|
// Default Debian data dir exists.
|
||||||
|
// So this is where we should migrate from.
|
||||||
|
dataDirMirage = dirDeb;
|
||||||
|
} else {
|
||||||
|
// No /matrix-mirage found, so check /mirage
|
||||||
|
QDir dir(genericData + "/mirage");
|
||||||
|
if (dir.exists())
|
||||||
|
// Default data dir exists.
|
||||||
|
// So this is where we should migrate from.
|
||||||
|
dataDirMirage = dir;
|
||||||
|
else
|
||||||
|
// No Mirage data dir found.
|
||||||
|
// Do not migrate.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
offerMigrateFromMirage(
|
||||||
|
configDirMoment, configDirMirage, dataDirMoment, dataDirMirage
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool setLockFile(QString configPath) {
|
bool setLockFile(QString configPath) {
|
||||||
|
|
||||||
QDir settingsFolder(configPath);
|
QDir settingsFolder(configPath);
|
||||||
|
@ -130,13 +362,19 @@ int main(int argc, char *argv[]) {
|
||||||
qInstallMessageHandler(loggingHandler);
|
qInstallMessageHandler(loggingHandler);
|
||||||
|
|
||||||
// Define some basic info about the app before creating the QApplication
|
// Define some basic info about the app before creating the QApplication
|
||||||
QApplication::setOrganizationName("mirage");
|
QApplication::setOrganizationName("moment");
|
||||||
QApplication::setApplicationName("mirage");
|
QApplication::setApplicationName("moment");
|
||||||
QApplication::setApplicationDisplayName("Mirage");
|
QApplication::setApplicationDisplayName("Moment");
|
||||||
QApplication::setApplicationVersion("0.7.2");
|
QApplication::setApplicationVersion("0.7.3");
|
||||||
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||||
|
|
||||||
QString customConfigDir(qEnvironmentVariable("MIRAGE_CONFIG_DIR"));
|
// app needs to be constructed before attempting to migrate
|
||||||
|
// because migrate displays a popup dialog
|
||||||
|
QApplication app(argc, argv);
|
||||||
|
|
||||||
|
if (shouldMigrateFromMirage()) tryMigrateFromMirage();
|
||||||
|
|
||||||
|
QString customConfigDir(qEnvironmentVariable("MOMENT_CONFIG_DIR"));
|
||||||
QString settingsFolder(
|
QString settingsFolder(
|
||||||
customConfigDir.isEmpty() ?
|
customConfigDir.isEmpty() ?
|
||||||
QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation)
|
QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation)
|
||||||
|
@ -144,8 +382,8 @@ int main(int argc, char *argv[]) {
|
||||||
customConfigDir
|
customConfigDir
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Attempt to create a lockfile in the settings folder
|
||||||
if (! setLockFile(settingsFolder)) return EXIT_SUCCESS;
|
if (! setLockFile(settingsFolder)) return EXIT_SUCCESS;
|
||||||
QApplication app(argc, argv);
|
|
||||||
|
|
||||||
// Register handlers for quit signals, e.g. SIGINT/Ctrl-C in unix terminals
|
// Register handlers for quit signals, e.g. SIGINT/Ctrl-C in unix terminals
|
||||||
signal(SIGINT, onExitSignal);
|
signal(SIGINT, onExitSignal);
|
||||||
|
|
561
src/themes/Foliage.qpl
Normal file
|
@ -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://<path>, 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:
|
||||||
|
'<style type"text/css">\n' + styleSheet + '\n</style>\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,
|
||||||
|
)
|