Commit c4dce50a authored by Bilal Elmoussaoui's avatar Bilal Elmoussaoui

Merge branch 'bilelmoussaoui/fixes' into 'master'

Cleanup in preparation of the next gtk-rs release Closes #47, #46, #21, and #44 See merge request GNOME/gnome-tour!60
parents 8992d346 0ba10569
...@@ -4,7 +4,7 @@ _build/ ...@@ -4,7 +4,7 @@ _build/
build-aux/app build-aux/app
build-aux/.flatpak-builder/ build-aux/.flatpak-builder/
src/config.rs src/config.rs
src/static_resources.rs
*.ui.in~ *.ui.in~
*.ui~ *.ui~
.flatpak
.vscode
...@@ -26,7 +26,6 @@ rustfmt: ...@@ -26,7 +26,6 @@ rustfmt:
# Create blank versions of our configured files # Create blank versions of our configured files
# so rustfmt does not yell about non-existent files or completely empty files # so rustfmt does not yell about non-existent files or completely empty files
- echo -e "" >> src/config.rs - echo -e "" >> src/config.rs
- echo -e "" >> src/static_resources.rs
- rustc -Vv && cargo -Vv - rustc -Vv && cargo -Vv
- cargo fmt --version - cargo fmt --version
- cargo fmt --all -- --color=always --check - cargo fmt --all -- --color=always --check
...@@ -2,25 +2,13 @@ ...@@ -2,25 +2,13 @@
name = "gnome-tour" name = "gnome-tour"
version = "40.0.0" version = "40.0.0"
authors = ["Bilal Elmoussaoui <bil.elmoussaoui@gmail.com>"] authors = ["Bilal Elmoussaoui <bil.elmoussaoui@gmail.com>"]
edition = "2018" edition = "2021"
[features]
video = ["gst_player", "gst"]
[dependencies] [dependencies]
gtk = { package = "gtk4", version = "0.3", features= ["v4_2"]} gtk = { package = "gtk4", version = "0.4", features= ["v4_2"]}
log = "0.4" log = "0.4"
gettext-rs = { version = "0.6", features = ["gettext-system"] } gettext-rs = { version = "0.7", features = ["gettext-system"] }
libadwaita = "0.1.0-beta-1" adw = {package = "libadwaita", version = "0.1"}
pretty_env_logger = "0.4" pretty_env_logger = "0.4"
[dependencies.gst_player]
version = "0.17"
package = "gstreamer-player"
optional = true
[dependencies.gst]
version = "0.17"
package = "gstreamer"
optional = true
...@@ -9,24 +9,3 @@ ...@@ -9,24 +9,3 @@
![screenshot](data/resources/screenshots/screenshot1.png) ![screenshot](data/resources/screenshots/screenshot1.png)
</div> </div>
### Video Feature
Tour uses by default the logo of the distribution based on the info from `/etc/os-release`. The application comes with a feature to replace the logo with a welcome video shipped by the distribution.
To enable the feature, you need to build the application with
```bash
meson _builddir -Dvideo_path=/absolute/path/to/the/video.mp4
```
If you're testing the application using Builder, make sure to change the `config-opts` accordingly & give the application filesystem access so it can play the video file.
Example:
This needs to be added to the `gnome-tour` module
```json
"config-opts" : [
"-Dvideo_path=/home/username/to/the/video.mp4"
]
```
along with `--filesystem=home` in `finish-args`
#!/bin/sh
export MESON_BUILD_ROOT="$1"
export MESON_SOURCE_ROOT="$2"
export CARGO_TARGET_DIR="$MESON_BUILD_ROOT"/target
export CARGO_HOME="$MESON_BUILD_ROOT"/cargo-home
FEATURES="$6"
if [ $4 = "Devel" ]
then
echo "DEBUG MODE"
cargo build --manifest-path \
"$MESON_SOURCE_ROOT"/Cargo.toml $FEATURES && \
cp "$CARGO_TARGET_DIR"/debug/$5 $3
else
echo "RELEASE MODE"
cargo build --manifest-path \
"$MESON_SOURCE_ROOT"/Cargo.toml $FEATURES --release && \
cp "$CARGO_TARGET_DIR"/release/$5 $3
fi
#!/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("Updating desktop database...")
call(["update-desktop-database", path.join(DATA_DIR, 'applications')])
{ {
"app-id" : "org.gnome.TourDevel", "app-id": "org.gnome.TourDevel",
"runtime" : "org.gnome.Platform", "runtime": "org.gnome.Platform",
"runtime-version" : "master", "runtime-version": "master",
"sdk" : "org.gnome.Sdk", "sdk": "org.gnome.Sdk",
"sdk-extensions" : [ "sdk-extensions": [
"org.freedesktop.Sdk.Extension.rust-stable" "org.freedesktop.Sdk.Extension.rust-stable"
], ],
"command" : "gnome-tour", "command": "gnome-tour",
"tags" : [ "finish-args": [
"nightly"
],
"finish-args" : [
"--share=ipc", "--share=ipc",
"--socket=fallback-x11", "--socket=fallback-x11",
"--socket=wayland", "--socket=wayland",
"--device=dri" "--device=dri",
"--env=RUST_LOG=gnome_tour=debug"
], ],
"build-options" : { "build-options": {
"append-path" : "/usr/lib/sdk/rust-stable/bin", "append-path": "/usr/lib/sdk/rust-stable/bin",
"build-args" : [ "build-args": [
"--share=network" "--share=network"
], ]
"env" : {
"GTK_DEBUG" : "interactive",
"CARGO_HOME" : "/run/build/gnome-tour/cargo",
"RUST_BACKTRACE" : "1"
}
}, },
"modules" : [ "modules": [
{
"name" : "libadwaita",
"buildsystem" : "meson",
"sources" : [
{
"type" : "git",
"url" : "https://gitlab.gnome.org/GNOME/libadwaita.git",
"branch" : "main"
}
]
},
{ {
"name" : "gnome-tour", "name": "gnome-tour",
"buildsystem" : "meson", "buildsystem": "meson",
"config-opts" : [ "config-opts": [
"-Dprofile=development" "-Dprofile=development"
], ],
"sources" : [ "sources": [
{ {
"type" : "git", "type": "git",
"url" : "https://gitlab.gnome.org/GNOME/gnome-tour.git" "url": "https://gitlab.gnome.org/GNOME/gnome-tour.git"
} }
] ]
} }
......
...@@ -56,6 +56,7 @@ resources = gnome.compile_resources( ...@@ -56,6 +56,7 @@ resources = gnome.compile_resources(
'resources', 'resources',
'resources.gresource.xml', 'resources.gresource.xml',
gresource_bundle: true, gresource_bundle: true,
source_dir: meson.current_build_dir() source_dir: meson.current_build_dir(),
install: true,
install_dir: pkgdatadir,
) )
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
<component type="desktop-application"> <component type="desktop-application">
<id>@app-id@</id> <id>@app-id@</id>
<metadata_license>CC0</metadata_license> <metadata_license>CC0</metadata_license>
<project_license>GPL-3.0+</project_license> <project_license>GPL-3.0-or-later</project_license>
<name>Tour</name> <name>Tour</name>
<summary>GNOME Tour and Greeter</summary> <summary>GNOME Tour and Greeter</summary>
<description> <description>
......
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<gresources> <gresources>
<gresource prefix="/org/gnome/Tour/"> <gresource prefix="/org/gnome/Tour/">
<file compressed="true" alias="style.css">resources/style.css</file> <file compressed="true" alias="style.css">resources/style.css</file>
<file compressed="true" alias="welcome.svg">resources/assets/welcome.svg</file> <file compressed="true" preprocess="xml-stripblanks" alias="ui/paginator.ui">resources/ui/paginator.ui</file>
<file compressed="true" alias="overview.svg">resources/assets/overview.svg</file> <file compressed="true" preprocess="xml-stripblanks" alias="ui/window.ui">resources/ui/window.ui</file>
<file compressed="true" alias="search.svg">resources/assets/search.svg</file> <file compressed="true" alias="welcome.svg">resources/assets/welcome.svg</file>
<file compressed="true" alias="blank.svg">resources/assets/blank.svg</file> <file compressed="true" alias="overview.svg">resources/assets/overview.svg</file>
<file compressed="true" alias="workspaces.svg">resources/assets/workspaces.svg</file> <file compressed="true" alias="search.svg">resources/assets/search.svg</file>
<file compressed="true" alias="ready-to-go.svg">resources/assets/ready-to-go.svg</file> <file compressed="true" alias="blank.svg">resources/assets/blank.svg</file>
<file compressed="true" alias="hand-fg.svg">resources/assets/hand-fg.svg</file> <file compressed="true" alias="workspaces.svg">resources/assets/workspaces.svg</file>
<file compressed="true" alias="updown-bg.svg">resources/assets/updown-bg.svg</file> <file compressed="true" alias="ready-to-go.svg">resources/assets/ready-to-go.svg</file>
<file compressed="true" alias="leftright-bg.svg">resources/assets/leftright-bg.svg</file> <file compressed="true" alias="hand-fg.svg">resources/assets/hand-fg.svg</file>
<file compressed="true" alias="updown-bg.svg">resources/assets/updown-bg.svg</file>
<file compressed="true" alias="leftright-bg.svg">resources/assets/leftright-bg.svg</file>
</gresource>
<gresource prefix="/org/gnome/Tour/icons/scalable/actions">
<file compressed="true" alias="left-large-symbolic.svg">resources/left-large-symbolic.svg</file>
<file compressed="true" alias="right-large-symbolic.svg">resources/right-large-symbolic.svg</file>
</gresource> </gresource>
</gresources> </gresources>
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="16px" viewBox="0 0 16 16" width="16px"><filter id="a" height="100%" width="100%" x="0%" y="0%"><feColorMatrix in="SourceGraphic" type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/></filter><mask id="b"><g filter="url(#a)"><path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.3"/></g></mask><clipPath id="c"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><mask id="d"><g filter="url(#a)"><path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.05"/></g></mask><clipPath id="e"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><mask id="f"><g filter="url(#a)"><path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.05"/></g></mask><clipPath id="g"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><mask id="h"><g filter="url(#a)"><path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.05"/></g></mask><clipPath id="i"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><mask id="j"><g filter="url(#a)"><path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.05"/></g></mask><clipPath id="k"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><mask id="l"><g filter="url(#a)"><path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.05"/></g></mask><clipPath id="m"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><mask id="n"><g filter="url(#a)"><path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.05"/></g></mask><clipPath id="o"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><mask id="p"><g filter="url(#a)"><path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.3"/></g></mask><clipPath id="q"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><mask id="r"><g filter="url(#a)"><path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.5"/></g></mask><clipPath id="s"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><g clip-path="url(#c)" mask="url(#b)" transform="matrix(1 0 0 1 -980 -80)"><path d="m 562.460938 212.058594 h 10.449218 c -1.183594 0.492187 -1.296875 2.460937 0 3 h -10.449218 z m 0 0" fill="#2e3436"/></g><g clip-path="url(#e)" mask="url(#d)" transform="matrix(1 0 0 1 -980 -80)"><path d="m 16 748 h 1 v 1 h -1 z m 0 0" fill="#2e3436" fill-rule="evenodd"/></g><g clip-path="url(#g)" mask="url(#f)" transform="matrix(1 0 0 1 -980 -80)"><path d="m 17 747 h 1 v 1 h -1 z m 0 0" fill="#2e3436" fill-rule="evenodd"/></g><g clip-path="url(#i)" mask="url(#h)" transform="matrix(1 0 0 1 -980 -80)"><path d="m 18 750 h 1 v 1 h -1 z m 0 0" fill="#2e3436" fill-rule="evenodd"/></g><g clip-path="url(#k)" mask="url(#j)" transform="matrix(1 0 0 1 -980 -80)"><path d="m 16 750 h 1 v 1 h -1 z m 0 0" fill="#2e3436" fill-rule="evenodd"/></g><g clip-path="url(#m)" mask="url(#l)" transform="matrix(1 0 0 1 -980 -80)"><path d="m 17 751 h 1 v 1 h -1 z m 0 0" fill="#2e3436" fill-rule="evenodd"/></g><g clip-path="url(#o)" mask="url(#n)" transform="matrix(1 0 0 1 -980 -80)"><path d="m 19 751 h 1 v 1 h -1 z m 0 0" fill="#2e3436" fill-rule="evenodd"/></g><g clip-path="url(#q)" mask="url(#p)" transform="matrix(1 0 0 1 -980 -80)"><path d="m 136 776 v 7 h 7 v -7 z m 0 0" fill="#2e3436"/></g><g clip-path="url(#s)" mask="url(#r)" transform="matrix(1 0 0 1 -980 -80)"><path d="m 219 758 h 3 v 12 h -3 z m 0 0" fill="#2e3436"/></g><g fill="#474747"><path d="m 11.644531 2.707031 l -1.414062 -1.414062 l -6.707031 6.707031 l 6.707031 6.707031 l 1.414062 -1.414062 l -5.292969 -5.292969 z m 0 0"/><path d="m 10.9375 15 h 1 v -1 h -1 z m 0 0"/><path d="m 10.9375 2 h 1 v -1 h -1 z m 0 0"/><path d="m 10.9375 3 c 0.554688 0 1 -0.449219 1 -1 c 0 -0.554688 -0.445312 -1 -1 -1 s -1 0.445312 -1 1 c 0 0.550781 0.445312 1 1 1 z m 0 0"/><path d="m 10.9375 15 c 0.554688 0 1 -0.449219 1 -1 c 0 -0.554688 -0.445312 -1 -1 -1 s -1 0.445312 -1 1 c 0 0.550781 0.445312 1 1 1 z m 0 0"/></g></svg>
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="16px" viewBox="0 0 16 16" width="16px"><filter id="a" height="100%" width="100%" x="0%" y="0%"><feColorMatrix in="SourceGraphic" type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/></filter><mask id="b"><g filter="url(#a)"><path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.3"/></g></mask><clipPath id="c"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><mask id="d"><g filter="url(#a)"><path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.05"/></g></mask><clipPath id="e"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><mask id="f"><g filter="url(#a)"><path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.05"/></g></mask><clipPath id="g"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><mask id="h"><g filter="url(#a)"><path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.05"/></g></mask><clipPath id="i"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><mask id="j"><g filter="url(#a)"><path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.05"/></g></mask><clipPath id="k"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><mask id="l"><g filter="url(#a)"><path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.05"/></g></mask><clipPath id="m"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><mask id="n"><g filter="url(#a)"><path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.05"/></g></mask><clipPath id="o"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><mask id="p"><g filter="url(#a)"><path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.3"/></g></mask><clipPath id="q"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><mask id="r"><g filter="url(#a)"><path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.5"/></g></mask><clipPath id="s"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><g clip-path="url(#c)" mask="url(#b)" transform="matrix(1 0 0 1 -1000 -80)"><path d="m 562.460938 212.058594 h 10.449218 c -1.183594 0.492187 -1.296875 2.460937 0 3 h -10.449218 z m 0 0" fill="#2e3436"/></g><g clip-path="url(#e)" mask="url(#d)" transform="matrix(1 0 0 1 -1000 -80)"><path d="m 16 748 h 1 v 1 h -1 z m 0 0" fill="#2e3436" fill-rule="evenodd"/></g><g clip-path="url(#g)" mask="url(#f)" transform="matrix(1 0 0 1 -1000 -80)"><path d="m 17 747 h 1 v 1 h -1 z m 0 0" fill="#2e3436" fill-rule="evenodd"/></g><g clip-path="url(#i)" mask="url(#h)" transform="matrix(1 0 0 1 -1000 -80)"><path d="m 18 750 h 1 v 1 h -1 z m 0 0" fill="#2e3436" fill-rule="evenodd"/></g><g clip-path="url(#k)" mask="url(#j)" transform="matrix(1 0 0 1 -1000 -80)"><path d="m 16 750 h 1 v 1 h -1 z m 0 0" fill="#2e3436" fill-rule="evenodd"/></g><g clip-path="url(#m)" mask="url(#l)" transform="matrix(1 0 0 1 -1000 -80)"><path d="m 17 751 h 1 v 1 h -1 z m 0 0" fill="#2e3436" fill-rule="evenodd"/></g><g clip-path="url(#o)" mask="url(#n)" transform="matrix(1 0 0 1 -1000 -80)"><path d="m 19 751 h 1 v 1 h -1 z m 0 0" fill="#2e3436" fill-rule="evenodd"/></g><g clip-path="url(#q)" mask="url(#p)" transform="matrix(1 0 0 1 -1000 -80)"><path d="m 136 776 v 7 h 7 v -7 z m 0 0" fill="#2e3436"/></g><g clip-path="url(#s)" mask="url(#r)" transform="matrix(1 0 0 1 -1000 -80)"><path d="m 219 758 h 3 v 12 h -3 z m 0 0" fill="#2e3436"/></g><g fill="#474747"><path d="m 4.292969 2.707031 l 1.414062 -1.414062 l 6.707031 6.707031 l -6.707031 6.707031 l -1.414062 -1.414062 l 5.292969 -5.292969 z m 0 0"/><path d="m 5 15 h -1 v -1 h 1 z m 0 0"/><path d="m 5 2 h -1 v -1 h 1 z m 0 0"/><path d="m 5 3 c -0.554688 0 -1 -0.449219 -1 -1 c 0 -0.554688 0.445312 -1 1 -1 s 1 0.445312 1 1 c 0 0.550781 -0.445312 1 -1 1 z m 0 0"/><path d="m 5 15 c -0.554688 0 -1 -0.449219 -1 -1 c 0 -0.554688 0.445312 -1 1 -1 s 1 0.445312 1 1 c 0 0.550781 -0.445312 1 -1 1 z m 0 0"/></g></svg>
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="PaginatorWidget" parent="GtkBox">
<child>
<object class="GtkHeaderBar">
<property name="show-title-buttons">True</property>
<property name="title-widget">
<object class="AdwCarouselIndicatorDots" id="carousel_dots">
<property name="carousel">carousel</property>
</object>
</property>
<style>
<class name="flat" />
</style>
</object>
</child>
<child>
<object class="GtkOverlay" id="previous_overlay">
<property name="valign">center</property>
<child type="overlay">
<object class="GtkButton" id="previous_btn">
<property name="margin-start">12</property>
<property name="icon-name">left-large-symbolic</property>
<property name="halign">start</property>
<property name="valign">center</property>
<property name="action-name">app.previous-page</property>
<property name="tooltip-text" translatable="yes">Previous</property>
<style>
<class name="circular" />
</style>
</object>
</child>
<child type="overlay">
<object class="GtkButton" id="next_btn">
<property name="margin-end">12</property>
<property name="icon-name">right-large-symbolic</property>
<property name="halign">end</property>
<property name="valign">center</property>
<property name="action-name">app.next-page</property>
<property name="tooltip-text" translatable="yes">Next</property>
<style>
<class name="circular" />
</style>
</object>
</child>
<child type="overlay">
<object class="GtkButton" id="start_btn">
<property name="margin-end">12</property>
<property name="icon-name">right-large-symbolic</property>
<property name="halign">end</property>
<property name="valign">center</property>
<property name="action-name">app.start-tour</property>
<property name="tooltip-text" translatable="yes">Start</property>
<style>
<class name="suggested-action" />
<class name="circular" />
</style>
</object>
</child>
<child>
<object class="AdwCarousel" id="carousel">
<property name="hexpand">True</property>
<property name="vexpand">True</property>
</object>
</child>
</object>
</child>
</template>
</interface>
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="Window" parent="AdwApplicationWindow">
<property name="default-width">960</property>
<property name="default-height">720</property>
<property name="content">
<object class="PaginatorWidget" id="paginator">
<child>
<object class="ImagePageWidget">
<property name="resource-uri">/org/gnome/Tour/overview.svg</property>
<property name="head" translatable="yes">Get an Overview</property>
<property name="body" translatable="yes">Press the Super key to see open windows and apps.</property>
</object>
</child>
<child>
<object class="ImagePageWidget">
<property name="resource-uri">/org/gnome/Tour/search.svg</property>
<property name="head" translatable="yes">Just Type to Search</property>
<property name="body" translatable="yes">Type in the overview to search. Launch apps, find things.</property>
</object>
</child>
<child>
<object class="ImagePageWidget">
<property name="resource-uri">/org/gnome/Tour/workspaces.svg</property>
<property name="head" translatable="yes">Keep on Top with Workspaces</property>
<property name="body" translatable="yes">Easily organize windows with the workspaces view.</property>
</object>
</child>
<child>
<object class="ImagePageWidget">
<property name="resource-uri">/org/gnome/Tour/blank.svg</property>
<property name="head" translatable="yes">Up/Down for the Overview</property>
<property name="body" translatable="yes">On a touchpad, use three-finger vertical swipes. Try it!</property>
</object>
</child>
<child>
<object class="ImagePageWidget">
<property name="resource-uri">/org/gnome/Tour/blank.svg</property>
<property name="head" translatable="yes">Left/Right for Workspaces</property>
<property name="body" translatable="yes">On a touchpad, use three-finger horizontal swipes. Try it!</property>
</object>
</child>
<child>
<object class="ImagePageWidget">
<property name="resource-uri">/org/gnome/Tour/ready-to-go.svg</property>
<property name="head" translatable="yes">That's it. Have a nice day!</property>
<property name="body" translatable="yes">To get more advice and tips, see the Help app.</property>
<style>
<class name="last-page" />
</style>
</object>
</child>
</object>
</property>
</template>
</interface>
project('gnome-tour', project('gnome-tour',
'rust', 'rust',
version: '41.rc', version: '41.rc',
meson_version : '>= 0.50') license: 'GPL-3.0-or-later',
meson_version : '>= 0.59')
i18n = import('i18n') i18n = import('i18n')
gnome = import('gnome') gnome = import('gnome')
...@@ -14,17 +15,10 @@ dependency('gdk-pixbuf-2.0') ...@@ -14,17 +15,10 @@ dependency('gdk-pixbuf-2.0')
dependency('gtk4', version: '>= 4.4') dependency('gtk4', version: '>= 4.4')
dependency('libadwaita-1', version: '>= 1') dependency('libadwaita-1', version: '>= 1')
if get_option('video_path') != ''
dependency('gstreamer-1.0', version: '>= 1.12')
dependency('gstreamer-video-1.0', version: '>= 1.12')
dependency('gstreamer-player-1.0', version: '>= 1.12')
endif
glib_compile_resources = find_program('glib-compile-resources', required: true) glib_compile_resources = find_program('glib-compile-resources', required: true)
desktop_file_validate = find_program('desktop-file-validate', required: false) desktop_file_validate = find_program('desktop-file-validate', required: false)
appstream_util = find_program('appstream-util', required: false) appstream_util = find_program('appstream-util', required: false)
cargo = find_program('cargo', required: false) cargo = find_program('cargo')
cargo_script = find_program('build-aux/cargo.sh')
version = meson.project_version() version = meson.project_version()
...@@ -35,7 +29,7 @@ localedir = prefix / get_option('localedir') ...@@ -35,7 +29,7 @@ localedir = prefix / get_option('localedir')
datadir = prefix / get_option('datadir') datadir = prefix / get_option('datadir')
pkgdatadir = datadir / meson.project_name() pkgdatadir = datadir / meson.project_name()
iconsdir = datadir / 'icons' iconsdir = datadir / 'icons'
podir =meson.source_root () / 'po' podir =meson.project_source_root () / 'po'
gettext_package = meson.project_name() gettext_package = meson.project_name()
...@@ -57,8 +51,8 @@ application_id = '@0@@1@'.format(base_id, profile) ...@@ -57,8 +51,8 @@ application_id = '@0@@1@'.format(base_id, profile)
meson.add_dist_script( meson.add_dist_script(
'build-aux/dist-vendor.sh', 'build-aux/dist-vendor.sh',
meson.build_root() / 'meson-dist' / meson.project_name() + '-' + version, meson.project_build_root() / 'meson-dist' / meson.project_name() + '-' + version,
meson.source_root() meson.project_source_root()
) )
if get_option('profile') == 'development' if get_option('profile') == 'development'
...@@ -71,4 +65,8 @@ subdir('data') ...@@ -71,4 +65,8 @@ subdir('data')
subdir('po') subdir('po')
subdir('src') subdir('src')
meson.add_install_script('build-aux/meson_post_install.py') gnome.post_install(
gtk_update_icon_cache: true,
update_desktop_database: true,
)
...@@ -9,9 +9,3 @@ option ( ...@@ -9,9 +9,3 @@ option (
description: 'The build profile for GNOME Tour. One of "default" or "development".' description: 'The build profile for GNOME Tour. One of "default" or "development".'
) )
option(
'video_path',
type : 'string',
value: '',
description : 'Sets the absolute path of a welcome video'
)
data/org.gnome.Tour.desktop.in.in data/org.gnome.Tour.desktop.in.in
data/org.gnome.Tour.metainfo.xml.in.in data/org.gnome.Tour.metainfo.xml.in.in
src/main.rs data/resources/ui/paginator.ui
src/widgets/pages/welcome.rs data/resources/ui/window.ui
src/widgets/image_page.rs
src/widgets/paginator.rs src/widgets/paginator.rs
src/widgets/window.rs src/main.rs
use crate::config; use crate::config;
use crate::utils; use crate::utils;
use crate::widgets::Window; use crate::widgets::Window;
use gtk::gio::{self, prelude::*}; use adw::prelude::*;
use gtk::glib::{self, clone}; use gtk::{
use gtk::prelude::*; gio,
glib::{self, clone},
subclass::prelude::*,
};
use log::info; use log::info;
use std::{cell::RefCell, rc::Rc};
pub struct Application { mod imp {
app: libadwaita::Application, use super::*;
window: RefCell<Rc<Option<Window>>>, use adw::subclass::prelude::*;
} use gtk::glib::{once_cell::sync::OnceCell, WeakRef};
impl Application {
pub fn new() -> Rc<Self> {
let app =
libadwaita::Application::new(Some(config::APP_ID), gio::ApplicationFlags::FLAGS_NONE);
app.set_resource_base_path(Some("/org/gnome/Tour"));
let application = Rc::new(Self { #[derive(Debug, Default)]
app, pub struct Application {
window: RefCell::new(Rc::new(None)), pub(super) window: OnceCell<WeakRef<Window>>,
}); }
application.setup_signals(application.clone()); #[glib::object_subclass]
application impl ObjectSubclass for Application {
const NAME: &'static str = "Application";
type ParentType = adw::Application;
type Type = super::Application;
} }
fn setup_gactions(&self, application: Rc<Self>) { impl ObjectImpl for Application {}
// Quit impl ApplicationImpl for Application {
utils::action( fn activate(&self, application: &Self::Type) {
&self.app, let window = Window::new(application);
"quit", application.add_window(&window);
clone!(@strong self.app as app => move |_, _| { window.present();
app.quit(); self.window.set(window.downgrade()).unwrap();
}), self.parent_activate(application);
); }
fn startup(&self, application: &Self::Type) {
// Quit
utils::action(
application,
"quit",
clone!(@weak application => move |_, _| {
application.quit();
}),
);
// Start Tour // Start Tour
utils::action( utils::action(
&self.app, application,
"start-tour", "start-tour",
clone!(@strong application => move |_, _| { clone!(@weak application => move |_, _| {
if let Some(window) = &*application.window.borrow().clone() { application.window().start_tour();
window.start_tour(); }),
} );
}),
);
// Skip Tour // Skip Tour
utils::action( utils::action(
&self.app, application,
"skip-tour", "skip-tour",
clone!(@strong self.app as app => move |_, _| { clone!(@weak application => move |_, _| {
app.quit(); application.quit();
}), }),
); );
utils::action( utils::action(
&self.app, application,
"next-page", "next-page",
clone!(@strong application => move |_, _| { clone!(@weak application => move |_, _| {
if let Some(window) = &*application.window.borrow().clone() { let window = application.window();
if window.paginator.borrow_mut().try_next().is_none() { if window.paginator().try_next().is_none() {
window.widget.close(); window.close();
} }
} }),
}), );
);
utils::action( utils::action(
&self.app, application,
"previous-page", "previous-page",
clone!(@strong application => move |_, _| { clone!(@weak application => move |_, _| {
if let Some(window) = &*application.window.borrow().clone() { let window = application.window();
if window.paginator.borrow_mut().try_previous().is_none() { if window.paginator().try_previous().is_none() {
window.reset_tour(); window.reset_tour();
} }
} }),
}), );
); application.set_accels_for_action("app.quit", &["<Control>q"]);
application.set_accels_for_action("app.skip-tour", &["Escape"]);
self.parent_startup(application);
}
}
impl GtkApplicationImpl for Application {}
impl AdwApplicationImpl for Application {}
}
glib::wrapper! {
pub struct Application(ObjectSubclass<imp::Application>)
@extends gio::Application, gtk::Application, adw::Application,
@implements gio::ActionMap, gio::ActionGroup;
}
self.app.set_accels_for_action("app.quit", &["<primary>q"]); impl Application {
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
glib::Object::new(&[
("application-id", &config::APP_ID),
("resource-base-path", &Some("/org/gnome/Tour")),
])
.unwrap()
} }
fn setup_signals(&self, app: Rc<Self>) { fn window(&self) -> Window {
self.app.connect_startup(clone!(@weak app => move |_| { self.imp().window.get().and_then(|w| w.upgrade()).unwrap()
app.setup_gactions(app.clone());
}));
self.app
.connect_activate(clone!(@weak app => move |gtk_app| {
let window = Window::new(&gtk_app);
gtk_app.add_window(&window.widget);
window.widget.present();
window.widget.show();
app.window.replace(Rc::new(Some(window)));
}));
} }
pub fn run(&self) { pub fn run() {
info!("GNOME Tour ({})", config::APP_ID); info!("GNOME Tour ({})", config::APP_ID);
info!("Version: {} ({})", config::VERSION, config::PROFILE); info!("Version: {} ({})", config::VERSION, config::PROFILE);
info!("Datadir: {}", config::PKGDATADIR); info!("Datadir: {}", config::PKGDATADIR);
let app = Self::new();
self.app.run(); gtk::prelude::ApplicationExtManual::run(&app);
} }
} }
...@@ -4,5 +4,4 @@ pub static PROFILE: &str = @PROFILE@; ...@@ -4,5 +4,4 @@ pub static PROFILE: &str = @PROFILE@;
pub static VERSION: &str = @VERSION@; pub static VERSION: &str = @VERSION@;
pub static GETTEXT_PACKAGE: &str = @GETTEXT_PACKAGE@; pub static GETTEXT_PACKAGE: &str = @GETTEXT_PACKAGE@;
pub static LOCALEDIR: &str = @LOCALEDIR@; pub static LOCALEDIR: &str = @LOCALEDIR@;
#[cfg(feature = "video")] pub const RESOURCES_FILE: &str = concat!(@PKGDATADIR@, "/resources.gresource");
pub static VIDEO_PATH: &str = @VIDEO_PATH@;
use gettextrs::*; use gettextrs::*;
use gtk::glib; use gtk::{gio, glib};
mod application; mod application;
mod config; mod config;
mod static_resources;
mod utils; mod utils;
mod widgets; mod widgets;
...@@ -14,24 +13,18 @@ fn main() { ...@@ -14,24 +13,18 @@ fn main() {
pretty_env_logger::init(); pretty_env_logger::init();
// Prepare i18n // Prepare i18n
setlocale(LocaleCategory::LcAll, ""); setlocale(LocaleCategory::LcAll, "");
bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR).expect(&format!( bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR)
"Unable to bind text domain for {}", .unwrap_or_else(|_| panic!("Unable to bind text domain for {}", GETTEXT_PACKAGE));
GETTEXT_PACKAGE textdomain(GETTEXT_PACKAGE)
)); .unwrap_or_else(|_| panic!("Unable to switch to text domain {}", GETTEXT_PACKAGE));
textdomain(GETTEXT_PACKAGE).expect(&format!(
"Unable to switch to text domain {}",
GETTEXT_PACKAGE
));
glib::set_application_name(&gettext("Tour")); glib::set_application_name(&gettext("Tour"));
glib::set_prgname(Some("Tour")); glib::set_prgname(Some("Tour"));
gtk::init().expect("Unable to start GTK3"); gtk::init().expect("Unable to start GTK3");
#[cfg(feature = "video")]
gst::init().expect("Unable to start gst");
static_resources::init().expect("Failed to initialize the resource file."); let res = gio::Resource::load(config::RESOURCES_FILE).expect("Could not load resources");
gio::resources_register(&res);
let app = Application::new(); Application::run()
app.run();
} }
...@@ -5,7 +5,6 @@ global_conf.set_quoted('PROFILE', profile) ...@@ -5,7 +5,6 @@ global_conf.set_quoted('PROFILE', profile)
global_conf.set_quoted('VERSION', version + version_suffix) global_conf.set_quoted('VERSION', version + version_suffix)
global_conf.set_quoted('GETTEXT_PACKAGE', gettext_package) global_conf.set_quoted('GETTEXT_PACKAGE', gettext_package)
global_conf.set_quoted('LOCALEDIR', localedir) global_conf.set_quoted('LOCALEDIR', localedir)
global_conf.set_quoted('VIDEO_PATH', get_option('video_path'))
config = configure_file( config = configure_file(
input: 'config.rs.in', input: 'config.rs.in',
output: 'config.rs', output: 'config.rs',
...@@ -14,62 +13,42 @@ config = configure_file( ...@@ -14,62 +13,42 @@ config = configure_file(
# Copy the config.rs output to the source directory. # Copy the config.rs output to the source directory.
run_command( run_command(
'cp', 'cp',
meson.build_root() / 'src' / 'config.rs', meson.project_build_root() / 'src' / 'config.rs',
meson.source_root() / 'src' / 'config.rs', meson.project_source_root() / 'src' / 'config.rs',
check: true check: true
) )
# include_bytes! only takes a string literal
resource_conf = configuration_data()
resource_conf.set_quoted('RESOURCEFILE', resources.full_path())
resource_rs = configure_file(
input: 'static_resources.rs.in',
output: 'static_resources.rs',
configuration: resource_conf
)
run_command( cargo_options = [ '--manifest-path', meson.project_source_root() / 'Cargo.toml' ]
'cp', cargo_options += [ '--target-dir', meson.project_build_root() / 'src' ]
resource_rs,
meson.current_source_dir(),
check: true
)
sources = files( if get_option('profile') == 'default'
'widgets/pages/image.rs', cargo_options += [ '--release' ]
'widgets/pages/mod.rs', rust_target = 'release'
'widgets/pages/welcome.rs', message('Building in release mode')
'widgets/mod.rs', else
'widgets/paginator.rs', rust_target = 'debug'
'widgets/window.rs', message('Building in debug mode')
'application.rs',
'config.rs',
'main.rs',
'static_resources.rs',
'utils.rs',
)
features = ''
if get_option('video_path') != ''
features = '--features video'
endif endif
custom_target(
cargo_env = [ 'CARGO_HOME=' + meson.project_build_root() / 'cargo-home' ]
cargo_build = custom_target(
'cargo-build', 'cargo-build',
build_by_default: true, build_by_default: true,
input: sources, build_always_stale: true,
output: meson.project_name(), output: meson.project_name(),
console: true, console: true,
install: true, install: true,
install_dir: bindir, install_dir: bindir,
depends: resources, depends: resources,
command: [ command: [
cargo_script, 'env',
meson.build_root(), cargo_env,
meson.source_root(), cargo, 'build',
'@OUTPUT@', cargo_options,
profile, '&&',
meson.project_name(), 'cp', 'src' / rust_target / meson.project_name(), '@OUTPUT@',
features
] ]
) )
// Source: https://gitlab.gnome.org/World/podcasts/blob/master/podcasts-gtk/src/static_resource.rs
use gtk::gio::{resources_register, Resource};
use gtk::glib::{Bytes, Error};
pub(crate) fn init() -> Result<(), Error> {
// load the gresource binary at build time and include/link it into the final
// binary.
let res_bytes = include_bytes!(@RESOURCEFILE@);
// Create Resource it will live as long the value lives.
let gbytes = Bytes::from_static(res_bytes.as_ref());
let resource = Resource::from_data(&gbytes)?;
// Register the resource so it won't be dropped and will continue to live in
// memory.
resources_register(&resource);
Ok(())
}
use gtk::glib;
use gtk::prelude::*;
use gtk::subclass::prelude::*;
mod imp {
use super::*;
use glib::once_cell::sync::Lazy;
use glib::{ParamFlags, ParamSpec, ParamSpecString, Value};
use gtk::glib::once_cell::sync::OnceCell;
#[derive(Debug, Default)]
pub struct ImagePageWidget {
pub(super) resource_uri: OnceCell<String>,
pub(super) head: OnceCell<String>,
pub(super) body: OnceCell<String>,
pub(super) picture: gtk::Picture,
}
#[glib::object_subclass]
impl ObjectSubclass for ImagePageWidget {
const NAME: &'static str = "ImagePageWidget";
type ParentType = gtk::Box;
type Type = super::ImagePageWidget;
}
impl ObjectImpl for ImagePageWidget {
fn constructed(&self, obj: &Self::Type) {
let layout_manager = obj
.layout_manager()
.map(|l| l.downcast::<gtk::BoxLayout>().unwrap())
.unwrap();
layout_manager.set_orientation(gtk::Orientation::Vertical);
obj.add_css_class("page");
obj.set_hexpand(true);
obj.set_vexpand(true);
obj.set_halign(gtk::Align::Fill);
obj.set_valign(gtk::Align::Fill);
let container = gtk::Box::builder()
.orientation(gtk::Orientation::Vertical)
.spacing(12)
.halign(gtk::Align::Center)
.valign(gtk::Align::Center)
.vexpand(true)
.margin_bottom(48)
.margin_top(12)
.margin_start(12)
.margin_end(12)
.build();
let clamp = adw::Clamp::new();
clamp.set_child(Some(&container));
self.picture.set_can_shrink(false);
self.picture.set_keep_aspect_ratio(true);
container.append(&self.picture);
let head_label = gtk::Label::builder()
.justify(gtk::Justification::Center)
.valign(gtk::Align::Center)
.margin_top(36)
.build();
obj.bind_property("head", &head_label, "label")
.flags(glib::BindingFlags::SYNC_CREATE)
.build();
head_label.add_css_class("title-1");
container.append(&head_label);
let body_label = gtk::Label::builder()
.lines(2)
.wrap(true)
.justify(gtk::Justification::Center)
.valign(gtk::Align::Center)
.margin_top(12)
.build();
obj.bind_property("body", &body_label, "label")
.flags(glib::BindingFlags::SYNC_CREATE)
.build();
container.append(&body_label);
obj.append(&clamp);
self.parent_constructed(obj);
}
fn properties() -> &'static [ParamSpec] {
static PROPERTIES: Lazy<Vec<ParamSpec>> = Lazy::new(|| {
vec![
ParamSpecString::new(
"resource-uri",
"Resource URI",
"Resource URI of the image",
None,
ParamFlags::READWRITE | ParamFlags::CONSTRUCT_ONLY,
),
ParamSpecString::new(
"head",
"Head",
"The title of the page",
None,
ParamFlags::READWRITE | ParamFlags::CONSTRUCT_ONLY,
),
ParamSpecString::new(
"body",
"Body",
"The body of the page",
None,
ParamFlags::READWRITE | ParamFlags::CONSTRUCT_ONLY,
),
]
});
PROPERTIES.as_ref()
}
fn set_property(&self, _obj: &Self::Type, _id: usize, value: &Value, pspec: &ParamSpec) {
match pspec.name() {
"resource-uri" => {
let resource_uri: String = value.get().unwrap();
self.picture.set_resource(Some(&resource_uri));
self.resource_uri.set(resource_uri).unwrap();
}
"head" => {
let head = value.get().unwrap();
self.head.set(head).unwrap();
}
"body" => {
let body = value.get().unwrap();
self.body.set(body).unwrap();
}
_ => unimplemented!(),
}
}
fn property(&self, _obj: &Self::Type, _id: usize, pspec: &ParamSpec) -> Value {
match pspec.name() {
"resource-uri" => self.resource_uri.get().to_value(),
"head" => self.head.get().to_value(),
"body" => self.body.get().to_value(),
_ => unimplemented!(),
}
}
}
impl WidgetImpl for ImagePageWidget {}
impl BoxImpl for ImagePageWidget {}
}
glib::wrapper! {
pub struct ImagePageWidget(ObjectSubclass<imp::ImagePageWidget>)
@extends gtk::Widget, gtk::Box;
}
impl ImagePageWidget {
pub fn new(resource_uri: &str, head: String, body: String) -> Self {
glib::Object::new::<Self>(&[
("resource-uri", &resource_uri),
("head", &head),
("body", &body),
])
.unwrap()
}
}
mod pages; mod image_page;
mod paginator; mod paginator;
mod window;
mod window;
pub use image_page::ImagePageWidget;
pub use window::Window; pub use window::Window;
use gtk::prelude::*;
pub struct ImagePageWidget {
pub widget: gtk::Box,
}
impl ImagePageWidget {
pub fn new(resource_uri: &str, head: String, body: String) -> Self {
let widget = gtk::Box::new(gtk::Orientation::Vertical, 0);
let image_page = Self { widget };
image_page.init(resource_uri, head, body);
image_page
}
fn init(&self, resource_uri: &str, head: String, body: String) {
self.widget.set_hexpand(true);
self.widget.set_vexpand(true);
self.widget.add_css_class("page");
self.widget.set_halign(gtk::Align::Fill);
self.widget.set_valign(gtk::Align::Fill);
let container = gtk::Box::builder()
.orientation(gtk::Orientation::Vertical)
.spacing(12)
.halign(gtk::Align::Center)
.valign(gtk::Align::Center)
.vexpand(true)
.margin_bottom(48)
.margin_top(12)
.margin_start(12)
.margin_end(12)
.build();
let clamp = libadwaita::Clamp::new();
clamp.set_child(Some(&container));
let picture = gtk::Picture::builder()
.can_shrink(false)
.keep_aspect_ratio(true)
.build();
picture.set_resource(Some(resource_uri));
container.append(&picture);
let head_label = gtk::Label::builder()
.label(&head)
.justify(gtk::Justification::Center)
.valign(gtk::Align::Center)
.margin_top(36)
.build();
head_label.add_css_class("title-1");
container.append(&head_label);
let body_label = gtk::Label::builder()
.label(&body)
.lines(2)
.wrap(true)
.justify(gtk::Justification::Center)
.valign(gtk::Align::Center)
.margin_top(12)
.build();
container.append(&body_label);
self.widget.append(&clamp);
}
}
mod image;
mod welcome;
pub use image::ImagePageWidget;
pub use welcome::WelcomePageWidget;
#[cfg(feature = "video")]
use crate::config;
use crate::utils::i18n_f;
use gettextrs::gettext;
#[cfg(feature = "video")]
use gio::FileExt;
use gtk::glib;
#[cfg(feature = "video")]
use gtk::glib::clone;
#[cfg(feature = "video")]
use gtk::glib::{Receiver, Sender};
use gtk::prelude::*;
#[cfg(feature = "video")]
use std::cell::RefCell;
#[derive(PartialEq)]
#[cfg(feature = "video")]
pub enum Action {
VideoReady,
VideoUp,
}
pub struct WelcomePageWidget {
pub widget: gtk::Box,
#[cfg(feature = "video")]
player: gst_player::Player,
#[cfg(feature = "video")]
receiver: RefCell<Option<Receiver<Action>>>,
#[cfg(feature = "video")]
sender: Sender<Action>,
}
impl WelcomePageWidget {
pub fn new() -> Self {
let widget = gtk::Box::new(gtk::Orientation::Horizontal, 0);
#[cfg(feature = "video")]
let player = {
let dispatcher = gst_player::PlayerGMainContextSignalDispatcher::new(None);
let sink = gst::ElementFactory::make("gtksink", None)
.expect("Missing dependency: element gtksink is needed (usually, in gstreamer-plugins-good or in gst-plugin-gtk).");
let renderer = gst_player::PlayerVideoOverlayVideoRenderer::with_sink(&sink).upcast();
gst_player::Player::new(
Some(&renderer),
Some(&dispatcher.upcast::<gst_player::PlayerSignalDispatcher>()),
)
};
#[cfg(feature = "video")]
let (sender, r) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
#[cfg(feature = "video")]
let receiver = RefCell::new(Some(r));
let welcome_page = Self {
widget,
#[cfg(feature = "video")]
player,
#[cfg(feature = "video")]
sender,
#[cfg(feature = "video")]
receiver,
};
welcome_page.init();
welcome_page
}
fn init(&self) {
let container = gtk::Box::builder()
.orientation(gtk::Orientation::Vertical)
.spacing(0)
.hexpand(true)
.vexpand(true)
.valign(gtk::Align::Center)
.halign(gtk::Align::Center)
.margin_top(24)
.margin_bottom(24)
.build();
self.widget.add_css_class("page");
self.widget.add_css_class("welcome-page");
let clamp = libadwaita::Clamp::new();
clamp.set_child(Some(&container));
#[cfg(not(feature = "video"))]
let header = {
let logo = gtk::Picture::builder()
.can_shrink(false)
.keep_aspect_ratio(true)
.build();
logo.set_resource(Some("/org/gnome/Tour/welcome.svg"));
logo.upcast::<gtk::Widget>()
};
#[cfg(feature = "video")]
let header = {
let video_widget = self
.player
.get_pipeline()
.get_property("video-sink")
.unwrap()
.get::<gst::Element>()
.expect("The player of a VideoPlayerWidget should not use the default sink.")
.unwrap()
.get_property("widget")
.unwrap()
.get::<gtk::Widget>()
.unwrap()
.unwrap();
video_widget.set_size_request(-1, 360);
video_widget.set_property("ignore-alpha", &false).unwrap();
video_widget.show();
video_widget.add_css_class("video");
video_widget
};
container.append(&header);
#[cfg(feature = "video")]
{
let receiver = self.receiver.borrow_mut().take().unwrap();
receiver.attach(
None,
clone!(@strong self.player as player => move |action| {
match action {
Action::VideoReady => player.play(),
Action::VideoUp => header.add_css_class("playing"),
};
glib::Continue(true)
}),
);
self.player.connect_state_changed(
clone!(@strong self.sender as sender => move |_p,state| {
if state == gst_player::PlayerState::Playing {
sender.send(Action::VideoUp).unwrap();
}
}),
);
self.player.connect_uri_loaded(
clone!(@strong self.sender as sender => move |_p, _uri| {
sender.send(Action::VideoReady).unwrap();
}),
);
self.player.connect_end_of_stream(move |p| p.stop());
let video_file = gio::File::new_for_path(config::VIDEO_PATH);
gtk::timeout_add(
500,
clone!(@strong self.player as player => move || {
player.set_uri(&video_file.get_uri());
glib::Continue(false)
}),
);
};
let title = gtk::Label::new(Some(&gettext("Start the Tour")));
title.set_margin_top(36);
title.add_css_class("title-1");
container.append(&title);
let name = glib::os_info("NAME").unwrap_or_else(|| "GNOME".into());
let version = glib::os_info("VERSION").unwrap_or_else(|| "".into());
// Translators: The following string is formated as "Learn about new and essential features in GNOME 3.36" for example
let text = gtk::Label::new(Some(&i18n_f(
"Learn about the key features in {} {}.",
&[&name, &version],
)));
text.add_css_class("body");
text.set_margin_top(12);
container.append(&text);
self.widget.append(&clamp);
}
}
use gettextrs::gettext; use adw::prelude::*;
use gtk::glib; use gtk::subclass::prelude::*;
use gtk::prelude::*; use gtk::{gio, glib};
use libadwaita::traits::ApplicationWindowExt;
use std::cell::RefCell;
use std::rc::Rc;
use super::pages::{ImagePageWidget, WelcomePageWidget};
use super::paginator::PaginatorWidget; use super::paginator::PaginatorWidget;
use crate::config::{APP_ID, PROFILE}; use crate::Application;
pub struct Window { mod imp {
pub widget: libadwaita::ApplicationWindow, use super::*;
pub paginator: RefCell<Rc<PaginatorWidget>>, use crate::config;
} use crate::widgets::ImagePageWidget;
use adw::subclass::prelude::*;
impl Window {
pub fn new(app: &libadwaita::Application) -> Self { #[derive(Debug, Default, gtk::CompositeTemplate)]
let widget = libadwaita::ApplicationWindow::new(app); #[template(resource = "/org/gnome/Tour/ui/window.ui")]
pub struct Window {
let paginator = RefCell::new(PaginatorWidget::new()); #[template_child]
pub(super) paginator: TemplateChild<PaginatorWidget>,
let mut window_widget = Window { widget, paginator };
window_widget.init();
window_widget
} }
pub fn start_tour(&self) { #[glib::object_subclass]
self.paginator.borrow_mut().set_page(1); impl ObjectSubclass for Window {
} const NAME: &'static str = "Window";
type Type = super::Window;
type ParentType = adw::ApplicationWindow;
pub fn reset_tour(&self) { fn class_init(klass: &mut Self::Class) {
self.paginator.borrow_mut().set_page(0); ImagePageWidget::static_type();
Self::bind_template(klass);
}
fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
obj.init_template();
}
} }
fn init(&mut self) { impl ObjectImpl for Window {
self.widget.set_default_size(960, 720); fn constructed(&self, widget: &Self::Type) {
self.widget.set_icon_name(Some(APP_ID)); widget.set_icon_name(Some(config::APP_ID));
// Devel Profile // Devel Profile
if PROFILE == "Devel" { if config::PROFILE == "Devel" {
self.widget.add_css_class("devel"); widget.add_css_class("devel");
}
self.parent_constructed(widget);
} }
self.paginator }
.borrow_mut() impl WidgetImpl for Window {}
.add_page(WelcomePageWidget::new().widget.upcast::<gtk::Widget>()); impl WindowImpl for Window {}
self.paginator.borrow_mut().add_page( impl ApplicationWindowImpl for Window {}
ImagePageWidget::new( impl AdwApplicationWindowImpl for Window {}
"/org/gnome/Tour/overview.svg", }
gettext("Get an Overview"),
gettext("Press the Super key to see open windows and apps."),
)
.widget
.upcast::<gtk::Widget>(),
);
self.paginator.borrow_mut().add_page(
ImagePageWidget::new(
"/org/gnome/Tour/search.svg",
gettext("Just Type to Search"),
gettext("Type in the overview to search. Launch apps, find things."),
)
.widget
.upcast::<gtk::Widget>(),
);
self.paginator.borrow_mut().add_page( glib::wrapper! {
ImagePageWidget::new( pub struct Window(ObjectSubclass<imp::Window>)
"/org/gnome/Tour/workspaces.svg", @extends gtk::Widget, gtk::Window, gtk::ApplicationWindow, adw::ApplicationWindow,
gettext("Keep on Top with Workspaces"), @implements gio::ActionMap, gio::ActionGroup;
gettext("Easily organize windows with the workspaces view."), }
)
.widget
.upcast::<gtk::Widget>(),
);
self.paginator.borrow_mut().add_page( impl Window {
ImagePageWidget::new( pub fn new(app: &Application) -> Self {
"/org/gnome/Tour/blank.svg", glib::Object::new(&[("application", app)]).unwrap()
gettext("Up/Down for the Overview"), }
gettext("On a touchpad, use three-finger vertical swipes. Try it!"),
)
.widget
.upcast::<gtk::Widget>(),
);
self.paginator.borrow_mut().add_page( pub fn paginator(&self) -> PaginatorWidget {
ImagePageWidget::new( self.imp().paginator.clone()
"/org/gnome/Tour/blank.svg", }
gettext("Left/Right for Workspaces"),
gettext("On a touchpad, use three-finger horizontal swipes. Try it!"),
)
.widget
.upcast::<gtk::Widget>(),
);
let last_page = ImagePageWidget::new( pub fn start_tour(&self) {
"/org/gnome/Tour/ready-to-go.svg", self.imp().paginator.set_page(1);
gettext("That's it. Have a nice day!"), }
gettext("To get more advice and tips, see the Help app."),
);
last_page.widget.add_css_class("last-page");
self.paginator
.borrow_mut()
.add_page(last_page.widget.upcast::<gtk::Widget>());
self.widget pub fn reset_tour(&self) {
.set_content(Some(&self.paginator.borrow().widget)); self.imp().paginator.set_page(0);
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment