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
 | 
					 | 
				
			||||||
[](https://gitlab.com/mx-moment/moment/-/releases)
 | 
					[](https://gitlab.com/mx-moment/moment/-/releases)
 | 
				
			||||||
[](https://github.com/poljar/matrix-nio)
 | 
					[](https://github.com/poljar/matrix-nio)
 | 
				
			||||||
[](https://matrix.to/#/#moment-client:matrix.org)
 | 
					[](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).
 | 
				
			||||||
 | 
					
 | 
				
			||||||

 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Currently Implemented Features
 | 
					## Currently Implemented Features
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -96,10 +95,10 @@ see differences [here](docs/MIRAGEDIFF.md).
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
## Screenshots
 | 
					## Screenshots
 | 
				
			||||||
 | 
					
 | 
				
			||||||

 | 
					
 | 
				
			||||||

 | 
					
 | 
				
			||||||

 | 
					
 | 
				
			||||||

 | 
					
 | 
				
			||||||

 | 
					
 | 
				
			||||||

 | 
					
 | 
				
			||||||

 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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"
 | 
				
			||||||
        response = await session.get(api_list)
 | 
					        try:
 | 
				
			||||||
 | 
					            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,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||