commit
49dab8eeaa
34 changed files with 1452 additions and 0 deletions
@ -0,0 +1,13 @@
|
||||
root = true |
||||
[*] |
||||
indent_style = space |
||||
trim_trailing_whitespace = true |
||||
insert_final_newline = true |
||||
charset = utf-8 |
||||
|
||||
[*.{build,yml,ui,yaml}] |
||||
indent_size = 2 |
||||
|
||||
[*.{json,py}] |
||||
indent_size = 4 |
||||
|
||||
@ -0,0 +1,10 @@
|
||||
target/ |
||||
build/ |
||||
_build/ |
||||
builddir/ |
||||
build-aux/app |
||||
build-aux/.flatpak-builder/ |
||||
src/config.rs |
||||
*.ui.in~ |
||||
*.ui~ |
||||
|
||||
@ -0,0 +1,39 @@
|
||||
stages: |
||||
- check |
||||
- test |
||||
|
||||
flatpak: |
||||
image: 'registry.gitlab.gnome.org/gnome/gnome-runtime-images/rust_bundle:3.38' |
||||
stage: test |
||||
tags: |
||||
- flatpak |
||||
variables: |
||||
BUNDLE: "fractal-nightly.flatpak" |
||||
MANIFEST_PATH: "build-aux/org.gnome.Fractal.Devel.json" |
||||
FLATPAK_MODULE: "fractal" |
||||
APP_ID: "org.gnome.Fractal.Devel" |
||||
RUNTIME_REPO: "https://nightly.gnome.org/gnome-nightly.flatpakrepo" |
||||
script: |
||||
- > |
||||
xvfb-run -a -s "-screen 0 1024x768x24" |
||||
flatpak-builder --keep-build-dirs --user --disable-rofiles-fuse flatpak_app --repo=repo ${BRANCH:+--default-branch=$BRANCH} ${MANIFEST_PATH} |
||||
- flatpak build-bundle repo ${BUNDLE} --runtime-repo=${RUNTIME_REPO} ${APP_ID} ${BRANCH} |
||||
artifacts: |
||||
name: 'Flatpak artifacts' |
||||
expose_as: 'Get Flatpak bundle here' |
||||
when: 'always' |
||||
paths: |
||||
- "${BUNDLE}" |
||||
- '.flatpak-builder/build/${FLATPAK_MODULE}/_flatpak_build/meson-logs/meson-log.txt' |
||||
- '.flatpak-builder/build/${FLATPAK_MODULE}/_flatpak_build/meson-logs/testlog.txt' |
||||
expire_in: 14 days |
||||
|
||||
# Configure and run rustfmt |
||||
# Exits and builds fails if on bad format |
||||
rustfmt: |
||||
image: "rust:slim" |
||||
script: |
||||
- rustup component add rustfmt |
||||
- rustc -Vv && cargo -Vv |
||||
- cargo fmt --version |
||||
- cargo fmt --all -- --color=always --check |
||||
@ -0,0 +1,17 @@
|
||||
[package] |
||||
name = "fractal" |
||||
version = "0.1.0" |
||||
authors = ["Daniel García Moreno <danigm@wadobo.com>"] |
||||
edition = "2018" |
||||
|
||||
[dependencies] |
||||
log = "0.4" |
||||
pretty_env_logger = "0.4" |
||||
gettext-rs = { version = "0.5", features = ["gettext-system"] } |
||||
gtk-macros = "0.2" |
||||
once_cell = "1.5" |
||||
|
||||
[dependencies.gtk] |
||||
package = "gtk4" |
||||
git = "https://github.com/gtk-rs/gtk4-rs" |
||||
rev = "abea0c9980bc083494eceb30dfab5eeb99a73118" |
||||
@ -0,0 +1,7 @@
|
||||
Copyright © 2019, Daniel García Moreno |
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: |
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. |
||||
|
||||
The Software is provided “as is”, without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders X be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the software or the use or other dealings in the Software. |
||||
Except as contained in this notice, the name of the Daniel García Moreno shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the Daniel García Moreno. |
||||
@ -0,0 +1,55 @@
|
||||
# GTK + Rust + Meson + Flatpak = <3 |
||||
|
||||
A boilerplate template to get started with GTK, Rust, Meson, Flatpak made for GNOME. It can be adapted for other desktop environments like elementary. |
||||
|
||||
<div align="center"> |
||||
 |
||||
</div> |
||||
|
||||
## What does it contains? |
||||
|
||||
- A simple window with a headerbar |
||||
- Bunch of useful files that you SHOULD ship with your application on Linux: |
||||
- Metainfo: describe your application for the different application stores out there; |
||||
- Desktop: the application launcher; |
||||
- Icons: This repo contains three icons, a normal, a nightly & monochromatic icon (symbolic) per the GNOME HIG, exported using [App Icon Preview](https://flathub.org/apps/details/org.gnome.design.AppIconPreview). |
||||
- Flatpak Manifest for nightly builds |
||||
- Dual installation support |
||||
- Uses Meson for building the application |
||||
- Bundles the UI files & the CSS using gresources |
||||
- A pre-commit hook to run rustfmt on your code |
||||
- Tests to validate your Metainfo, Schemas & Desktop files |
||||
- Gsettings to store the window state, more settings could be added |
||||
- Gitlab CI to produce flatpak nightlies |
||||
- i18n support |
||||
|
||||
## How to init a project ? |
||||
|
||||
The template ships a simple python script to init a project easily. It asks you a few questions and replaces & renames all the necessary files. |
||||
|
||||
The script requires having `git` installed on your system. |
||||
|
||||
You can run it with, |
||||
|
||||
```shell |
||||
python3 create-project.py |
||||
``` |
||||
|
||||
```shell |
||||
➜ python3 create-project.py |
||||
Welcome to GTK Rust Template |
||||
Name: Contrast |
||||
Project Name: contrast |
||||
Application ID (see: https://developer.gnome.org/ChooseApplicationID/): org.gnome.design.Contrast |
||||
Author: Bilal Elmoussaoui |
||||
Email: bil.elmoussaoui@gmail.com |
||||
Github/Gitlab repository: https://gitlab.gnome.org/World/design/contrast/ |
||||
Use gtk4 [Y/n]: y |
||||
``` |
||||
|
||||
A new directory named `contrast` containing the generated project |
||||
|
||||
## Credits |
||||
|
||||
- [Podcasts](https://gitlab.gnome.org/World/podcasts) |
||||
- [Shortwave](https://gitlab.gnome.org/World/Shortwave) |
||||
@ -0,0 +1,20 @@
|
||||
#!/bin/sh |
||||
|
||||
export MESON_BUILD_ROOT="$1" |
||||
export MESON_SOURCE_ROOT="$2" |
||||
export CARGO_TARGET_DIR="$MESON_BUILD_ROOT"/target |
||||
export CARGO_HOME="$CARGO_TARGET_DIR"/cargo-home |
||||
|
||||
if [[ $4 = "Devel" ]] |
||||
then |
||||
echo "DEBUG MODE" |
||||
cargo build --manifest-path \ |
||||
"$MESON_SOURCE_ROOT"/Cargo.toml && \ |
||||
cp "$CARGO_TARGET_DIR"/debug/$5 $3 |
||||
else |
||||
echo "RELEASE MODE" |
||||
cargo build --manifest-path \ |
||||
"$MESON_SOURCE_ROOT"/Cargo.toml --release && \ |
||||
cp "$CARGO_TARGET_DIR"/release/$5 $3 |
||||
fi |
||||
|
||||
@ -0,0 +1,10 @@
|
||||
#!/bin/bash |
||||
export DIST="$1" |
||||
export SOURCE_ROOT="$2" |
||||
|
||||
cd "$SOURCE_ROOT" |
||||
mkdir "$DIST"/.cargo |
||||
cargo vendor | sed 's/^directory = ".*"/directory = "vendor"/g' > $DIST/.cargo/config |
||||
# Move vendor into dist tarball directory |
||||
mv vendor "$DIST" |
||||
|
||||
@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env python3 |
||||
|
||||
from os import environ, path |
||||
from subprocess import call |
||||
|
||||
if not environ.get('DESTDIR', ''): |
||||
PREFIX = environ.get('MESON_INSTALL_PREFIX', '/usr/local') |
||||
DATA_DIR = path.join(PREFIX, 'share') |
||||
print('Updating icon cache...') |
||||
call(['gtk-update-icon-cache', '-qtf', path.join(DATA_DIR, 'icons/hicolor')]) |
||||
print("Compiling new schemas...") |
||||
call(["glib-compile-schemas", path.join(DATA_DIR, 'glib-2.0/schemas')]) |
||||
print("Updating desktop database...") |
||||
call(["update-desktop-database", path.join(DATA_DIR, 'applications')]) |
||||
@ -0,0 +1,104 @@
|
||||
{ |
||||
"app-id": "org.gnome.Fractal.Devel", |
||||
"runtime": "org.gnome.Platform", |
||||
"runtime-version": "3.38", |
||||
"sdk": "org.gnome.Sdk", |
||||
"sdk-extensions": ["org.freedesktop.Sdk.Extension.rust-stable"], |
||||
"command": "fractal", |
||||
"finish-args" : [ |
||||
"--socket=fallback-x11", |
||||
"--socket=wayland", |
||||
"--device=dri", |
||||
"--talk-name=org.a11y.Bus", |
||||
"--env=RUST_LOG=fractal=debug", |
||||
"--env=G_MESSAGES_DEBUG=none" |
||||
], |
||||
"build-options" : { |
||||
"append-path" : "/usr/lib/sdk/rust-stable/bin", |
||||
"build-args" : [ |
||||
"--share=network" |
||||
], |
||||
"test-args": [ |
||||
"--socket=x11", |
||||
"--share=network" |
||||
], |
||||
"env" : { |
||||
"CARGO_HOME" : "/run/build/fractal/cargo", |
||||
"RUST_BACKTRACE": "1", |
||||
"RUSTFLAGS": "-L=/app/lib" |
||||
} |
||||
}, |
||||
"modules": [ |
||||
{ |
||||
"name": "gtk4", |
||||
"buildsystem": "meson", |
||||
"config-opts": [ |
||||
"-Ddemos=false", |
||||
"-Dbuild-examples=false", |
||||
"-Dbuild-tests=false" |
||||
], |
||||
"sources": [ |
||||
{ |
||||
"type": "archive", |
||||
"url": "https://download.gnome.org/sources/gtk/4.0/gtk-4.0.0.tar.xz", |
||||
"sha256": "d46cf5b127ea27dd9e5d2ff6ed500cb4067eeb2cb1cd2c313ccde8013b0b9bf9" |
||||
} |
||||
], |
||||
"modules": [ |
||||
{ |
||||
"name": "pango", |
||||
"buildsystem": "meson", |
||||
"sources": [ |
||||
{ |
||||
"type": "archive", |
||||
"url": "https://download.gnome.org/sources/pango/1.48/pango-1.48.0.tar.xz", |
||||
"sha256": "391f26f3341c2d7053e0fb26a956bd42360dadd825efe7088b1e9340a65e74e6" |
||||
} |
||||
] |
||||
}, |
||||
{ |
||||
"name": "libsass", |
||||
"sources": [ |
||||
{ |
||||
"type": "archive", |
||||
"url": "https://github.com/sass/libsass/archive/3.6.4.tar.gz", |
||||
"sha256": "f9484d9a6df60576e791566eab2f757a97fd414fce01dd41fc0a693ea5db2889" |
||||
}, |
||||
{ |
||||
"type": "script", |
||||
"dest-filename": "autogen.sh", |
||||
"commands": ["autoreconf -si"] |
||||
} |
||||
] |
||||
}, |
||||
{ |
||||
"name": "sassc", |
||||
"sources": [ |
||||
{ |
||||
"type": "archive", |
||||
"url": "https://github.com/sass/sassc/archive/3.6.1.tar.gz", |
||||
"sha256": "8cee391c49a102b4464f86fc40c4ceac3a2ada52a89c4c933d8348e3e4542a60" |
||||
}, |
||||
{ |
||||
"type": "script", |
||||
"dest-filename": "autogen.sh", |
||||
"commands": ["autoreconf -si"] |
||||
} |
||||
] |
||||
} |
||||
] |
||||
}, |
||||
{ |
||||
"name": "fractal", |
||||
"buildsystem": "meson", |
||||
"run-tests": true, |
||||
"config-opts": ["-Dprofile=development"], |
||||
"sources": [ |
||||
{ |
||||
"type": "dir", |
||||
"path": "../" |
||||
} |
||||
] |
||||
} |
||||
] |
||||
} |
||||
|
After Width: | Height: | Size: 10 KiB |
@ -0,0 +1,10 @@
|
||||
install_data( |
||||
'@0@.svg'.format(application_id), |
||||
install_dir: iconsdir / 'hicolor' / 'scalable' / 'apps' |
||||
) |
||||
|
||||
install_data( |
||||
'@0@-symbolic.svg'.format(base_id), |
||||
install_dir: iconsdir / 'hicolor' / 'symbolic' / 'apps', |
||||
rename: '@0@-symbolic.svg'.format(application_id) |
||||
) |
||||
|
After Width: | Height: | Size: 4.3 KiB |
@ -0,0 +1,83 @@
|
||||
subdir('icons') |
||||
# Desktop file |
||||
desktop_conf = configuration_data() |
||||
desktop_conf.set('icon', application_id) |
||||
desktop_file = i18n.merge_file( |
||||
type: 'desktop', |
||||
input: configure_file( |
||||
input: '@0@.desktop.in.in'.format(base_id), |
||||
output: '@BASENAME@', |
||||
configuration: desktop_conf |
||||
), |
||||
output: '@0@.desktop'.format(application_id), |
||||
po_dir: podir, |
||||
install: true, |
||||
install_dir: datadir / 'applications' |
||||
) |
||||
# Validate Desktop file |
||||
if desktop_file_validate.found() |
||||
test( |
||||
'validate-desktop', |
||||
desktop_file_validate, |
||||
args: [ |
||||
desktop_file.full_path() |
||||
] |
||||
) |
||||
endif |
||||
|
||||
# Appdata |
||||
appdata_conf = configuration_data() |
||||
appdata_conf.set('app-id', application_id) |
||||
appdata_conf.set('gettext-package', gettext_package) |
||||
appdata_file = i18n.merge_file( |
||||
input: configure_file( |
||||
input: '@0@.metainfo.xml.in.in'.format(base_id), |
||||
output: '@BASENAME@', |
||||
configuration: appdata_conf |
||||
), |
||||
output: '@0@.metainfo.xml'.format(application_id), |
||||
po_dir: podir, |
||||
install: true, |
||||
install_dir: datadir / 'metainfo' |
||||
) |
||||
# Validate Appdata |
||||
if appstream_util.found() |
||||
test( |
||||
'validate-appdata', appstream_util, |
||||
args: [ |
||||
'validate', '--nonet', appdata_file.full_path() |
||||
] |
||||
) |
||||
endif |
||||
|
||||
# GSchema |
||||
gschema_conf = configuration_data() |
||||
gschema_conf.set('app-id', application_id) |
||||
gschema_conf.set('gettext-package', gettext_package) |
||||
configure_file( |
||||
input: '@0@.gschema.xml.in'.format(base_id), |
||||
output: '@0@.gschema.xml'.format(application_id), |
||||
configuration: gschema_conf, |
||||
install: true, |
||||
install_dir: datadir / 'glib-2.0' / 'schemas' |
||||
) |
||||
|
||||
# Validata GSchema |
||||
if glib_compile_schemas.found() |
||||
test( |
||||
'validate-gschema', glib_compile_schemas, |
||||
args: [ |
||||
'--strict', '--dry-run', meson.current_source_dir() |
||||
] |
||||
) |
||||
endif |
||||
|
||||
# Resources |
||||
resources = gnome.compile_resources( |
||||
'resources', |
||||
'resources.gresource.xml', |
||||
gresource_bundle: true, |
||||
source_dir: meson.current_build_dir(), |
||||
install: true, |
||||
install_dir: pkgdatadir, |
||||
) |
||||
@ -0,0 +1,11 @@
|
||||
[Desktop Entry] |
||||
Name=Fractal |
||||
Comment=A GTK + Rust application boilerplate template |
||||
Type=Application |
||||
Exec=fractal |
||||
Terminal=false |
||||
Categories=GNOME;GTK; |
||||
Keywords=Gnome;GTK; |
||||
# Translators: Do NOT translate or transliterate this text (this is an icon file name)! |
||||
Icon=@icon@ |
||||
StartupNotify=true |
||||
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<schemalist> |
||||
<schema path="/org/gnome/Fractal/" id="@app-id@" gettext-domain="@gettext-package@"> |
||||
<key name="window-width" type="i"> |
||||
<default>-1</default> |
||||
<summary>Default window width</summary> |
||||
<description>Default window width</description> |
||||
</key> |
||||
<key name="window-height" type="i"> |
||||
<default>-1</default> |
||||
<summary>Default window height</summary> |
||||
<description>Default window height</description> |
||||
</key> |
||||
<key name="is-maximized" type="b"> |
||||
<default>false</default> |
||||
<summary>Default window maximized behaviour</summary> |
||||
<description></description> |
||||
</key> |
||||
</schema> |
||||
</schemalist> |
||||
@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<!-- Daniel García Moreno 2019 <danigm@wadobo.com> --> |
||||
<component type="desktop-application"> |
||||
<id>@app-id@</id> |
||||
<metadata_license>CC0</metadata_license> |
||||
<project_license>GPL-3.0+</project_license> |
||||
<name>Fractal</name> |
||||
<summary>A GTK + Rust application template.</summary> |
||||
<description> |
||||
<p>A boilerplate template for GTK + Rust. It uses Meson as a build system and has flatpak support by default.</p> |
||||
</description> |
||||
<screenshots> |
||||
<screenshot type="default"> |
||||
<image>https://gitlab.gnome.org/GNOME/fractal/raw/master/data/resources/screenshots/screenshot1.png</image> |
||||
<caption>Main Window</caption> |
||||
</screenshot> |
||||
</screenshots> |
||||
<url type="homepage">https://gitlab.gnome.org/GNOME/fractal</url> |
||||
<url type="bugtracker">https://gitlab.gnome.org/GNOME/fractal/issues</url> |
||||
<content_rating type="oars-1.0" /> |
||||
<releases> |
||||
<release version="0.0.1" date="2019-07-11" /> |
||||
</releases> |
||||
<kudos> |
||||
<!-- |
||||
GNOME Software kudos: |
||||
https://gitlab.gnome.org/GNOME/gnome-software/blob/master/doc/kudos.md |
||||
--> |
||||
<kudo>ModernToolkit</kudo> |
||||
<kudo>HiDpiIcon</kudo> |
||||
</kudos> |
||||
<developer_name>Daniel García Moreno</developer_name> |
||||
<update_contact>danigm@wadobo.com</update_contact> |
||||
<translation type="gettext">@gettext-package@</translation> |
||||
<launchable type="desktop-id">@app-id@.desktop</launchable> |
||||
</component> |
||||
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<gresources> |
||||
<gresource prefix="/org/gnome/Fractal/"> |
||||
<file compressed="true" preprocess="xml-stripblanks" alias="shortcuts.ui">resources/ui/shortcuts.ui</file> |
||||
<file compressed="true" preprocess="xml-stripblanks" alias="window.ui">resources/ui/window.ui</file> |
||||
|
||||
<file compressed="true" alias="style.css">resources/style.css</file> |
||||
</gresource> |
||||
</gresources> |
||||
@ -0,0 +1,4 @@
|
||||
.title-header{ |
||||
font-size: 36px; |
||||
font-weight: bold; |
||||
} |
||||
@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<interface> |
||||
<object class="GtkShortcutsWindow" id="shortcuts"> |
||||
<property name="modal">True</property> |
||||
<child> |
||||
<object class="GtkShortcutsSection"> |
||||
<property name="section-name">shortcuts</property> |
||||
<property name="max-height">10</property> |
||||
<child> |
||||
<object class="GtkShortcutsGroup"> |
||||
<property name="title" translatable="yes" context="shortcut window">General</property> |
||||
<child> |
||||
<object class="GtkShortcutsShortcut"> |
||||
<property name="title" translatable="yes" context="shortcut window">Show Shortcuts</property> |
||||
<property name="accelerator"><Primary>question</property> |
||||
</object> |
||||
</child> |
||||
<child> |
||||
<object class="GtkShortcutsShortcut"> |
||||
<property name="title" translatable="yes" context="shortcut window">Quit</property> |
||||
<property name="accelerator"><Primary>Q</property> |
||||
</object> |
||||
</child> |
||||
</object> |
||||
</child> |
||||
</object> |
||||
</child> |
||||
</object> |
||||
</interface> |
||||
|
||||
@ -0,0 +1,40 @@
|
||||
<interface> |
||||
<menu id="primary_menu"> |
||||
<section> |
||||
<item> |
||||
<attribute name="label" translatable="yes">_Preferences</attribute> |
||||
<attribute name="action">app.preferences</attribute> |
||||
</item> |
||||
<item> |
||||
<attribute name="label" translatable="yes">_Keyboard Shortcuts</attribute> |
||||
<attribute name="action">win.show-help-overlay</attribute> |
||||
</item> |
||||
<item> |
||||
<attribute name="label" translatable="yes">_About GTK Rust Template</attribute> |
||||
<attribute name="action">app.about</attribute> |
||||
</item> |
||||
</section> |
||||
</menu> |
||||
<template class="ExampleApplicationWindow" parent="GtkApplicationWindow"> |
||||
<property name="default-width">600</property> |
||||
<property name="default-height">400</property> |
||||
<child type="titlebar"> |
||||
<object class="GtkHeaderBar" id="headerbar"> |
||||
<child type="end"> |
||||
<object class="GtkMenuButton" id="appmenu_button"> |
||||
<property name="icon-name">open-menu-symbolic</property> |
||||
<property name="menu-model">primary_menu</property> |
||||
</object> |
||||
</child> |
||||
</object> |
||||
</child> |
||||
<child> |
||||
<object class="GtkLabel" id="label"> |
||||
<property name="label" translatable="yes">Hello world!</property> |
||||
<style> |
||||
<class name="title-header"/> |
||||
</style> |
||||
</object> |
||||
</child> |
||||
</template> |
||||
</interface> |
||||
@ -0,0 +1,54 @@
|
||||
#!/bin/sh |
||||
# Source: https://gitlab.gnome.org/GNOME/fractal/blob/master/hooks/pre-commit.hook |
||||
|
||||
install_rustfmt() { |
||||
if ! which rustup &> /dev/null; then |
||||
curl https://sh.rustup.rs -sSf | sh -s -- -y |
||||
export PATH=$PATH:$HOME/.cargo/bin |
||||
if ! which rustup &> /dev/null; then |
||||
echo "Failed to install rustup. Performing the commit without style checking." |
||||
exit 0 |
||||
fi |
||||
fi |
||||
|
||||
if ! rustup component list|grep rustfmt &> /dev/null; then |
||||
echo "Installing rustfmt…" |
||||
rustup component add rustfmt |
||||
fi |
||||
} |
||||
|
||||
if ! which cargo &> /dev/null || ! cargo fmt --help &> /dev/null; then |
||||
echo "Unable to check Fractal’s code style, because rustfmt could not be run." |
||||
|
||||
if [ ! -t 1 ]; then |
||||
# No input is possible |
||||
echo "Performing commit." |
||||
exit 0 |
||||
fi |
||||
|
||||
echo "" |
||||
echo "y: Install rustfmt via rustup" |
||||
echo "n: Don't install rustfmt and perform the commit" |
||||
echo "Q: Don't install rustfmt and abort the commit" |
||||
|
||||
while true; do |
||||
read -p "Install rustfmt via rustup? [y/n/Q]: " yn |
||||
case $yn in |
||||
[Yy]* ) install_rustfmt; break;; |
||||
[Nn]* ) echo "Performing commit."; exit 0;; |
||||
[Qq]* | "" ) echo "Aborting commit."; exit -1;; |
||||
* ) echo "Invalid input";; |
||||
esac |
||||
done |
||||
fi |
||||
|
||||
echo "--Checking style--" |
||||
cargo fmt --all -- --check |
||||
if test $? != 0; then |
||||
echo "--Checking style fail--" |
||||
echo "Please fix the above issues, either manually or by running: cargo fmt --all" |
||||
|
||||
exit -1 |
||||
else |
||||
echo "--Checking style pass--" |
||||
fi |
||||
@ -0,0 +1,70 @@
|
||||
project('fractal', |
||||
'rust', |
||||
version: '0.0.1', |
||||
license: 'MIT', |
||||
meson_version: '>= 0.50') |
||||
|
||||
i18n = import('i18n') |
||||
gnome = import('gnome') |
||||
|
||||
base_id = 'org.gnome.Fractal' |
||||
|
||||
dependency('glib-2.0', version: '>= 2.66') |
||||
dependency('gio-2.0', version: '>= 2.66') |
||||
dependency('gtk4', version: '>= 4.0.0') |
||||
|
||||
glib_compile_resources = find_program('glib-compile-resources', required: true) |
||||
glib_compile_schemas = find_program('glib-compile-schemas', required: true) |
||||
desktop_file_validate = find_program('desktop-file-validate', required: false) |
||||
appstream_util = find_program('appstream-util', required: false) |
||||
cargo = find_program('cargo', required: true) |
||||
cargo_script = find_program('build-aux/cargo.sh') |
||||
|
||||
version = meson.project_version() |
||||
version_array = version.split('.') |
||||
major_version = version_array[0].to_int() |
||||
minor_version = version_array[1].to_int() |
||||
version_micro = version_array[2].to_int() |
||||
|
||||
prefix = get_option('prefix') |
||||
bindir = prefix / get_option('bindir') |
||||
localedir = prefix / get_option('localedir') |
||||
|
||||
datadir = prefix / get_option('datadir') |
||||
pkgdatadir = datadir / meson.project_name() |
||||
iconsdir = datadir / 'icons' |
||||
podir = meson.source_root() / 'po' |
||||
gettext_package = meson.project_name() |
||||
|
||||
if get_option('profile') == 'development' |
||||
profile = 'Devel' |
||||
vcs_tag = run_command('git', 'rev-parse', '--short', 'HEAD').stdout().strip() |
||||
if vcs_tag == '' |
||||
version_suffix = '-devel' |
||||
else |
||||
version_suffix = '-@0@'.format(vcs_tag) |
||||
endif |
||||
else |
||||
profile = '' |
||||
version_suffix = '' |
||||
endif |
||||
|
||||
application_id = '@0@.@1@'.format(base_id, profile) |
||||
|
||||
meson.add_dist_script( |
||||
'build-aux/dist-vendor.sh', |
||||
meson.build_root() / 'meson-dist' / meson.project_name() + '-' + version, |
||||
meson.source_root() |
||||
) |
||||
|
||||
if get_option('profile') == 'development' |
||||
# Setup pre-commit hook for ensuring coding style is always consistent |
||||
message('Setting up git pre-commit hook..') |
||||
run_command('cp', '-f', 'hooks/pre-commit.hook', '.git/hooks/pre-commit') |
||||
endif |
||||
|
||||
subdir('data') |
||||
subdir('po') |
||||
subdir('src') |
||||
|
||||
meson.add_install_script('build-aux/meson_post_install.py') |
||||
@ -0,0 +1,10 @@
|
||||
option( |
||||
'profile', |
||||
type: 'combo', |
||||
choices: [ |
||||
'default', |
||||
'development' |
||||
], |
||||
value: 'default', |
||||
description: 'The build profile for Fractal. One of "default" or "development".' |
||||
) |
||||
@ -0,0 +1,5 @@
|
||||
data/resources/ui/shortcuts.ui |
||||
data/resources/ui/window.ui.in |
||||
data/org.gnome.Fractal.desktop.in.in |
||||
data/org.gnome.Fractal.gschema.xml.in |
||||
data/org.gnome.Fractal.metainfo.xml.in.in |
||||
@ -0,0 +1,161 @@
|
||||
use crate::config; |
||||
use crate::window::ExampleApplicationWindow; |
||||
use gio::ApplicationFlags; |
||||
use glib::clone; |
||||
use glib::WeakRef; |
||||
use gtk::prelude::*; |
||||
use gtk::subclass::prelude::*; |
||||
use gtk::{gdk, gio, glib}; |
||||
use gtk_macros::action; |
||||
use log::{debug, info}; |
||||
use once_cell::sync::OnceCell; |
||||
use std::env; |
||||
|
||||
mod imp { |
||||
use super::*; |
||||
use glib::subclass; |
||||
|
||||
#[derive(Debug)] |
||||
pub struct ExampleApplication { |
||||
pub window: OnceCell<WeakRef<ExampleApplicationWindow>>, |
||||
} |
||||
|
||||
impl ObjectSubclass for ExampleApplication { |
||||
const NAME: &'static str = "ExampleApplication"; |
||||
type Type = super::ExampleApplication; |
||||
type ParentType = gtk::Application; |
||||
type Interfaces = (); |
||||
type Instance = subclass::simple::InstanceStruct<Self>; |
||||
type Class = subclass::simple::ClassStruct<Self>; |
||||
|
||||
glib::object_subclass!(); |
||||
|
||||
fn new() -> Self { |
||||
Self { |
||||
window: OnceCell::new(), |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl ObjectImpl for ExampleApplication {} |
||||
|
||||
impl gio::subclass::prelude::ApplicationImpl for ExampleApplication { |
||||
fn activate(&self, app: &Self::Type) { |
||||
debug!("GtkApplication<ExampleApplication>::activate"); |
||||
|
||||
let priv_ = ExampleApplication::from_instance(app); |
||||
if let Some(window) = priv_.window.get() { |
||||
let window = window.upgrade().unwrap(); |
||||
window.show(); |
||||
window.present(); |
||||
return; |
||||
} |
||||
|
||||
app.set_resource_base_path(Some("/org/gnome/Fractal/")); |
||||
app.setup_css(); |
||||
|
||||
let window = ExampleApplicationWindow::new(app); |
||||
self.window |
||||
.set(window.downgrade()) |
||||
.expect("Window already set."); |
||||
|
||||
app.setup_gactions(); |
||||
app.setup_accels(); |
||||
|
||||
app.get_main_window().present(); |
||||
} |
||||
|
||||
fn startup(&self, app: &Self::Type) { |
||||
debug!("GtkApplication<ExampleApplication>::startup"); |
||||
self.parent_startup(app); |
||||
} |
||||
} |
||||
|
||||
impl GtkApplicationImpl for ExampleApplication {} |
||||
} |
||||
|
||||
glib::wrapper! { |
||||
pub struct ExampleApplication(ObjectSubclass<imp::ExampleApplication>) |
||||
@extends gio::Application, gtk::Application, @implements gio::ActionMap, gio::ActionGroup; |
||||
} |
||||
|
||||
impl ExampleApplication { |
||||
pub fn new() -> Self { |
||||
glib::Object::new(&[ |
||||
("application-id", &Some(config::APP_ID)), |
||||
("flags", &ApplicationFlags::empty()), |
||||
]) |
||||
.expect("Application initialization failed...") |
||||
} |
||||
|
||||
fn get_main_window(&self) -> ExampleApplicationWindow { |
||||
let priv_ = imp::ExampleApplication::from_instance(self); |
||||
priv_.window.get().unwrap().upgrade().unwrap() |
||||
} |
||||
|
||||
fn setup_gactions(&self) { |
||||
// Quit
|
||||
action!( |
||||
self, |
||||
"quit", |
||||
clone!(@weak self as app => move |_, _| { |
||||
// This is needed to trigger the delete event
|
||||
// and saving the window state
|
||||
app.get_main_window().close(); |
||||
app.quit(); |
||||
}) |
||||
); |
||||
|
||||
// About
|
||||
action!( |
||||
self, |
||||
"about", |
||||
clone!(@weak self as app => move |_, _| { |
||||
app.show_about_dialog(); |
||||
}) |
||||
); |
||||
} |
||||
|
||||
// Sets up keyboard shortcuts
|
||||
fn setup_accels(&self) { |
||||
self.set_accels_for_action("app.quit", &["<primary>q"]); |
||||
self.set_accels_for_action("win.show-help-overlay", &["<primary>question"]); |
||||
} |
||||
|
||||
fn setup_css(&self) { |
||||
let provider = gtk::CssProvider::new(); |
||||
provider.load_from_resource("/org/gnome/Fractal/style.css"); |
||||
if let Some(display) = gdk::Display::get_default() { |
||||
gtk::StyleContext::add_provider_for_display( |
||||
&display, |
||||
&provider, |
||||
gtk::STYLE_PROVIDER_PRIORITY_APPLICATION, |
||||
); |
||||
} |
||||
} |
||||
|
||||
fn show_about_dialog(&self) { |
||||
let dialog = gtk::AboutDialogBuilder::new() |
||||
.program_name("Fractal") |
||||
.logo_icon_name(config::APP_ID) |
||||
.license_type(gtk::License::MitX11) |
||||
.website("https://gitlab.gnome.org/GNOME/fractal/") |
||||
.version(config::VERSION) |
||||
.transient_for(&self.get_main_window()) |
||||
.modal(true) |
||||
.authors(vec!["Daniel García Moreno".into()]) |
||||
.artists(vec!["Daniel García Moreno".into()]) |
||||
.build(); |
||||
|
||||
dialog.show(); |
||||
} |
||||
|
||||
pub fn run(&self) { |
||||
info!("Fractal ({})", config::APP_ID); |
||||
info!("Version: {} ({})", config::VERSION, config::PROFILE); |
||||
info!("Datadir: {}", config::PKGDATADIR); |
||||
|
||||
let args: Vec<String> = env::args().collect(); |
||||
ApplicationExtManual::run(self, &args); |
||||
} |
||||
} |
||||
@ -0,0 +1,7 @@
|
||||
pub const APP_ID: &str = @APP_ID@; |
||||
pub const GETTEXT_PACKAGE: &str = @GETTEXT_PACKAGE@; |
||||
pub const LOCALEDIR: &str = @LOCALEDIR@; |
||||
pub const PKGDATADIR: &str = @PKGDATADIR@; |
||||
pub const PROFILE: &str = @PROFILE@; |
||||
pub const RESOURCES_FILE: &str = concat!(@PKGDATADIR@, "/resources.gresource"); |
||||
pub const VERSION: &str = @VERSION@; |
||||
@ -0,0 +1,30 @@
|
||||
mod application; |
||||
#[rustfmt::skip] |
||||
mod config; |
||||
mod window; |
||||
|
||||
use application::ExampleApplication; |
||||
use config::{GETTEXT_PACKAGE, LOCALEDIR, RESOURCES_FILE}; |
||||
use gettextrs::*; |
||||
use gtk::gio; |
||||
|
||||
fn main() { |
||||
// Initialize logger, debug is carried out via debug!, info!, and warn!.
|
||||
pretty_env_logger::init(); |
||||
|
||||
// Prepare i18n
|
||||
setlocale(LocaleCategory::LcAll, ""); |
||||
bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR); |
||||
textdomain(GETTEXT_PACKAGE); |
||||
|
||||
gtk::glib::set_application_name("Fractal"); |
||||
gtk::glib::set_prgname(Some("fractal")); |
||||
|
||||
gtk::init().expect("Unable to start GTK4"); |
||||
|
||||
let res = gio::Resource::load(RESOURCES_FILE).expect("Could not load gresource file"); |
||||
gio::resources_register(&res); |
||||
|
||||
let app = ExampleApplication::new(); |
||||
app.run(); |
||||
} |
||||
@ -0,0 +1,45 @@
|
||||
global_conf = configuration_data() |
||||
global_conf.set_quoted('APP_ID', application_id) |
||||
global_conf.set_quoted('PKGDATADIR', pkgdatadir) |
||||
global_conf.set_quoted('PROFILE', profile) |
||||
global_conf.set_quoted('VERSION', version + version_suffix) |
||||
global_conf.set_quoted('GETTEXT_PACKAGE', gettext_package) |
||||
global_conf.set_quoted('LOCALEDIR', localedir) |
||||
config = configure_file( |
||||
input: 'config.rs.in', |
||||
output: 'config.rs', |
||||
configuration: global_conf |
||||
) |
||||
# Copy the config.rs output to the source directory. |
||||
run_command( |
||||
'cp', |
||||
meson.build_root() / 'src' / 'config.rs', |
||||
meson.source_root() / 'src' / 'config.rs', |
||||
check: true |
||||
) |
||||
|
||||
sources = files( |
||||
'application.rs', |
||||
'config.rs', |
||||
'main.rs', |
||||
'window.rs', |
||||
) |
||||
|
||||
custom_target( |
||||
'cargo-build', |
||||
build_by_default: true, |
||||
input: sources, |
||||
output: meson.project_name(), |
||||
console: true, |
||||
install: true, |
||||
install_dir: bindir, |
||||
depends: resources, |
||||
command: [ |
||||
cargo_script, |
||||
meson.build_root(), |
||||
meson.source_root(), |
||||
'@OUTPUT@', |
||||
profile, |
||||
meson.project_name(), |
||||
] |
||||
) |
||||
@ -0,0 +1,124 @@
|
||||
use crate::application::ExampleApplication; |
||||
use crate::config::{APP_ID, PROFILE}; |
||||
use glib::signal::Inhibit; |
||||
use gtk::subclass::prelude::*; |
||||
use gtk::{self, prelude::*}; |
||||
use gtk::{gio, glib, CompositeTemplate}; |
||||
use log::warn; |
||||
|
||||
mod imp { |
||||
use super::*; |
||||
use glib::subclass; |
||||
|
||||
#[derive(Debug, CompositeTemplate)] |
||||
#[template(resource = "/org/gnome/Fractal/window.ui")] |
||||
pub struct ExampleApplicationWindow { |
||||
#[template_child] |
||||
pub headerbar: TemplateChild<gtk::HeaderBar>, |
||||
pub settings: gio::Settings, |
||||
} |
||||
|
||||
impl ObjectSubclass for ExampleApplicationWindow { |
||||
const NAME: &'static str = "ExampleApplicationWindow"; |
||||
type Type = super::ExampleApplicationWindow; |
||||
type ParentType = gtk::ApplicationWindow; |
||||
type Interfaces = (); |
||||
type Instance = subclass::simple::InstanceStruct<Self>; |
||||
type Class = subclass::simple::ClassStruct<Self>; |
||||
|
||||
glib::object_subclass!(); |
||||
|
||||
fn new() -> Self { |
||||
Self { |
||||
headerbar: TemplateChild::default(), |
||||
settings: gio::Settings::new(APP_ID), |
||||
} |
||||
} |
||||
|
||||
fn class_init(klass: &mut Self::Class) { |
||||
Self::bind_template(klass); |
||||
} |
||||
|
||||
// You must call `Widget`'s `init_template()` within `instance_init()`.
|
||||
fn instance_init(obj: &glib::subclass::InitializingObject<Self::Type>) { |
||||
obj.init_template(); |
||||
} |
||||
} |
||||
|
||||
impl ObjectImpl for ExampleApplicationWindow { |
||||
fn constructed(&self, obj: &Self::Type) { |
||||
self.parent_constructed(obj); |
||||
|
||||
let builder = |
||||
gtk::Builder::from_resource("/org/gnome/Fractal/shortcuts.ui"); |
||||
let shortcuts = builder.get_object("shortcuts").unwrap(); |
||||
obj.set_help_overlay(Some(&shortcuts)); |
||||
|
||||
// Devel Profile
|
||||
if PROFILE == "Devel" { |
||||
obj.get_style_context().add_class("devel"); |
||||
} |
||||
|
||||
// load latest window state
|
||||
obj.load_window_size(); |
||||
} |
||||
} |
||||
|
||||
impl WindowImpl for ExampleApplicationWindow { |
||||
// save window state on delete event
|
||||
fn close_request(&self, obj: &Self::Type) -> Inhibit { |
||||
if let Err(err) = obj.save_window_size() { |
||||
warn!("Failed to save window state, {}", &err); |
||||
} |
||||
Inhibit(false) |
||||
} |
||||
} |
||||
|
||||
impl WidgetImpl for ExampleApplicationWindow {} |
||||
impl ApplicationWindowImpl for ExampleApplicationWindow {} |
||||
} |
||||
|
||||
glib::wrapper! { |
||||
pub struct ExampleApplicationWindow(ObjectSubclass<imp::ExampleApplicationWindow>) |
||||
@extends gtk::Widget, gtk::Window, gtk::ApplicationWindow, @implements gio::ActionMap, gio::ActionGroup; |
||||
} |
||||
|
||||
impl ExampleApplicationWindow { |
||||
pub fn new(app: &ExampleApplication) -> Self { |
||||
let window: Self = |
||||
glib::Object::new(&[]).expect("Failed to create ExampleApplicationWindow"); |
||||
window.set_application(Some(app)); |
||||
|
||||
// Set icons for shell
|
||||
gtk::Window::set_default_icon_name(APP_ID); |
||||
|
||||
window |
||||
} |
||||
|
||||
pub fn save_window_size(&self) -> Result<(), glib::BoolError> { |
||||
let settings = &imp::ExampleApplicationWindow::from_instance(self).settings; |
||||
|
||||
let size = self.get_default_size(); |
||||
|
||||
settings.set_int("window-width", size.0)?; |
||||
settings.set_int("window-height", size.1)?; |
||||
|
||||
settings.set_boolean("is-maximized", self.is_maximized())?; |
||||
|
||||
Ok(()) |
||||
} |
||||
|
||||
fn load_window_size(&self) { |
||||
let settings = &imp::ExampleApplicationWindow::from_instance(self).settings; |
||||
|
||||
let width = settings.get_int("window-width"); |
||||
let height = settings.get_int("window-height"); |
||||
let is_maximized = settings.get_boolean("is-maximized"); |
||||
|
||||
self.set_default_size(width, height); |
||||
|
||||
if is_maximized { |
||||
self.maximize(); |
||||
} |
||||
} |
||||
} |
||||
Loading…
Reference in new issue