mirror of
https://github.com/Rafostar/clapper.git
synced 2025-08-29 23:32:04 +02:00
Merge pull request #129 from Rafostar/libadwaita
Initialize with libadwaita, port preferences window to be adaptive and do few CSS tweaks to improve overall app look.
This commit is contained in:
31
README.md
31
README.md
@@ -30,16 +30,15 @@ The media player uses [GStreamer](https://gstreamer.freedesktop.org/) as a media
|
||||
|
||||
## Installation from Flatpak
|
||||
The `Flatpak` package includes all required dependencies and codecs.
|
||||
Additionally it also has a few patches, thus some functionalities work better (or are only available) in `Flatpak` version (until my changes are accepted upstream). List of patches used in this version can be found [here](https://github.com/Rafostar/clapper/issues/35).
|
||||
Additionally it also has a few patches, thus some functionalities work better (or are only available) in `Flatpak` version (until my changes are accepted upstream).
|
||||
List of patches used in this version can be found [here](https://github.com/Rafostar/clapper/issues/35).
|
||||
|
||||
<a href='https://flathub.org/apps/details/com.github.rafostar.Clapper'><img width='240' alt='Download on Flathub' src='https://flathub.org/assets/badges/flathub-badge-en.png'/></a>
|
||||
|
||||
**Important:** If you have been using the Flatpak package from my custom 3rd party repo, please remove it and replace your installation with version from Flathub. That repository will not be maintained any longer. Thank you for understanding.
|
||||
<a href='https://flathub.org/apps/details/com.github.rafostar.Clapper'>
|
||||
<img width='240' alt='Download on Flathub' src='https://flathub.org/assets/badges/flathub-badge-en.png'/>
|
||||
</a>
|
||||
|
||||
## Packages
|
||||
The [pkgs folder](https://github.com/Rafostar/clapper/tree/master/pkgs) in this repository contains build scripts for various package formats. You can use them to build package yourself or download one of pre-built packages:
|
||||
|
||||
#### Debian, Fedora & openSUSE
|
||||
#### Fedora & openSUSE
|
||||
Pre-built packages are available in [my repo](https://software.opensuse.org//download.html?project=home%3ARafostar&package=clapper) ([see status](https://build.opensuse.org/package/show/home:Rafostar/clapper)).<br>
|
||||
Those are automatically built on each git commit, and are thus considered unstable.
|
||||
|
||||
@@ -54,22 +53,12 @@ meson builddir --prefix=/usr/local
|
||||
sudo meson install -C builddir
|
||||
```
|
||||
|
||||
## Q&A
|
||||
**Q:** Does using `GJS` negatively impact video performance?<br>
|
||||
**A:** Absolutely not. `GJS` is only used to put together the GUI during startup.
|
||||
It has nothing to do with video rendering. All `GTK4` and `GStreamer` libraries are in C.
|
||||
Even the custom video widget that I prepared for this player (based on original `GTK3` implementation) is compiled C code.
|
||||
All of these libs are acting "on their own" and no function calls from `GJS` related to video decoding and rendering are performed during playback.
|
||||
|
||||
**Q:** What settings should I set to maximize performance?<br>
|
||||
**A:** As of now, player works best on `Wayland` session. `Wayland` users can try enabling highly experimental `vah264dec` plugin for improved performance (this plugin does not work on `Xorg` right now) for standard (8-bit) `H.264` videos.
|
||||
It can be enabled from inside the player preferences dialog inside `Advanced -> GStreamer` tab using the customizable `Plugin Ranking` feature.
|
||||
Since the whole app is rendered using your GPU, users of VERY weak GPUs might want to disable the "render window shadows" option to have more GPU power available for non-fullscreen video rendering.
|
||||
|
||||
## Other Questions?
|
||||
## Questions?
|
||||
Feel free to ask me any questions. Come and talk on Matrix: [#clapper-player:matrix.org](https://matrix.to/#/#clapper-player:matrix.org)
|
||||
|
||||
## Special Thanks
|
||||
Many thanks to [sp1ritCS](https://github.com/sp1ritCS) for creating and maintaining package build files. Big thanks to [bridadan](https://github.com/bridadan) and [Uniformbuffer3](https://github.com/Uniformbuffer3) for helping with testing V4L2 and NVDEC hardware acceleration methods.
|
||||
Many thanks to [sp1ritCS](https://github.com/sp1ritCS) for creating and maintaining package build files.
|
||||
Big thanks to [bridadan](https://github.com/bridadan) and [Uniformbuffer3](https://github.com/Uniformbuffer3) for helping
|
||||
with testing V4L2 and NVDEC hardware acceleration methods.
|
||||
|
||||
Thanks a lot to all the people who are supporting the development with their anonymous donations through [Liberapay](https://liberapay.com/Clapper/). I :heart: U.
|
||||
|
1
_service
1
_service
@@ -4,7 +4,6 @@
|
||||
<param name="url">https://github.com/Rafostar/clapper.git</param>
|
||||
<param name="extract">pkgs/rpm/clapper.spec</param>
|
||||
<param name="extract">pkgs/rpm/clapper.rpmlintrc</param>
|
||||
<param name="extract">pkgs/deb/clapper.dsc</param>
|
||||
</service>
|
||||
<service name="tar" mode="buildtime"/>
|
||||
<service name="recompress" mode="buildtime">
|
||||
|
@@ -6,18 +6,18 @@ radio {
|
||||
margin-left: -2px;
|
||||
}
|
||||
/* Adwaita is missing osd ListBox */
|
||||
.osd list {
|
||||
.playlistrow {
|
||||
border-radius: 5px;
|
||||
}
|
||||
.playlistrow {
|
||||
color: @theme_fg_color;
|
||||
}
|
||||
.osd .playlist {
|
||||
background: none;
|
||||
}
|
||||
.osd list row image {
|
||||
.osd .playlist row image {
|
||||
-gtk-icon-shadow: none;
|
||||
}
|
||||
.gtk402 trough highlight {
|
||||
border-color: transparent;
|
||||
}
|
||||
.gtk402 .osd trough highlight {
|
||||
border-color: inherit;
|
||||
}
|
||||
.osdheaderbar {
|
||||
background: transparent;
|
||||
}
|
||||
@@ -34,18 +34,15 @@ radio {
|
||||
.linkedright image {
|
||||
margin-right: 2px;
|
||||
}
|
||||
/* Non-osd style for popover menu */
|
||||
.menupopover label {
|
||||
color: @theme_text_color;
|
||||
}
|
||||
.menupopover arrow {
|
||||
background: @theme_base_color;
|
||||
border-color: @insensitive_base_color;
|
||||
}
|
||||
.menupopover contents {
|
||||
background: @theme_base_color;
|
||||
border-color: @insensitive_base_color;
|
||||
|
||||
/* Flat popovers */
|
||||
popover arrow,
|
||||
popover contents {
|
||||
border-color: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* Rounded corners */
|
||||
.adwrounded.csd {
|
||||
border-radius: 8px;
|
||||
}
|
||||
@@ -61,8 +58,15 @@ radio {
|
||||
.roundedcorners {
|
||||
border-radius: 8px;
|
||||
}
|
||||
.adwthemedark scale trough highlight {
|
||||
filter: brightness(120%);
|
||||
|
||||
/* Reduce sliders size */
|
||||
scale trough slider {
|
||||
min-height: 18px;
|
||||
min-width: 18px;
|
||||
}
|
||||
.fullscreen.tvmode scale trough slider {
|
||||
min-height: 20px;
|
||||
min-width: 20px;
|
||||
}
|
||||
|
||||
.videowidget {
|
||||
@@ -166,9 +170,6 @@ radio {
|
||||
margin-right: -4px;
|
||||
}
|
||||
.positionscale trough highlight {
|
||||
min-height: 4px;
|
||||
}
|
||||
.osd .positionscale trough highlight {
|
||||
min-height: 6px;
|
||||
}
|
||||
.fullscreen.tvmode .positionscale trough slider {
|
||||
@@ -176,6 +177,7 @@ radio {
|
||||
background: transparent;
|
||||
border-color: transparent;
|
||||
box-shadow: none;
|
||||
outline: none;
|
||||
}
|
||||
.positionscale mark indicator {
|
||||
min-height: 6px;
|
||||
@@ -232,6 +234,9 @@ radio {
|
||||
margin-top: -4px;
|
||||
margin-bottom: -6px;
|
||||
}
|
||||
.volumescale trough highlight {
|
||||
min-width: 4px;
|
||||
}
|
||||
.fullscreen.tvmode .volumescale trough highlight {
|
||||
min-width: 6px;
|
||||
}
|
||||
@@ -249,6 +254,9 @@ radio {
|
||||
.fullscreen.tvmode .elapsedpopoverbox {
|
||||
min-width: 360px;
|
||||
}
|
||||
.speedscale trough highlight {
|
||||
min-height: 4px;
|
||||
}
|
||||
.fullscreen.tvmode .speedscale trough highlight {
|
||||
min-height: 6px;
|
||||
}
|
||||
@@ -279,23 +287,6 @@ radio {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Preferences */
|
||||
.prefsnotebook grid {
|
||||
margin: 10px;
|
||||
}
|
||||
.prefssubpage header {
|
||||
background: none;
|
||||
}
|
||||
.prefssubpage header tabs tab {
|
||||
box-shadow: none;
|
||||
margin: 0px;
|
||||
margin-right: 1px;
|
||||
}
|
||||
.prefssubpage header tabs tab:checked {
|
||||
color: initial;
|
||||
background: @theme_selected_bg_color;
|
||||
}
|
||||
|
||||
/* Open URI Dialog */
|
||||
.uridialogbox {
|
||||
margin: 12px;
|
||||
|
@@ -6,35 +6,31 @@
|
||||
<default>false</default>
|
||||
<summary>Automatically enter fullscreen when first file is loaded</summary>
|
||||
</key>
|
||||
<key name="volume-initial" type="s">
|
||||
<default>"restore"</default>
|
||||
<summary>Mode used for startup volume value</summary>
|
||||
<key name="volume-custom" type="b">
|
||||
<default>false</default>
|
||||
<summary>Set custom volume value at startup</summary>
|
||||
</key>
|
||||
<key name="volume-value" type="i">
|
||||
<default>100</default>
|
||||
<summary>Custom initial volume value in percentage after startup</summary>
|
||||
</key>
|
||||
<key name="keep-last-frame" type="b">
|
||||
<default>false</default>
|
||||
<summary>Keep showing last video frame after playback finishes</summary>
|
||||
</key>
|
||||
<key name="close-auto" type="b">
|
||||
<default>false</default>
|
||||
<summary>Automatically close the app after playback finishes</summary>
|
||||
<key name="after-playback" type="i">
|
||||
<default>0</default>
|
||||
<summary>What to do after playback finishes</summary>
|
||||
</key>
|
||||
|
||||
<!-- Behaviour -->
|
||||
<key name="seeking-mode" type="s">
|
||||
<default>"normal"</default>
|
||||
<!-- Behavior -->
|
||||
<key name="seeking-mode" type="i">
|
||||
<default>0</default>
|
||||
<summary>Mode used for seeking</summary>
|
||||
</key>
|
||||
<key name="seeking-value" type="i">
|
||||
<default>10</default>
|
||||
<summary>Time amount to seek with single press of arrow keys</summary>
|
||||
</key>
|
||||
<key name="seeking-unit" type="s">
|
||||
<default>"second"</default>
|
||||
<summary>Unit to use with seeking value</summary>
|
||||
<key name="seeking-unit" type="i">
|
||||
<default>0</default>
|
||||
<summary>Unit ID to use with seeking value</summary>
|
||||
</key>
|
||||
<key name="resume-enabled" type="b">
|
||||
<default>true</default>
|
||||
@@ -50,13 +46,13 @@
|
||||
</key>
|
||||
|
||||
<!-- Audio -->
|
||||
<key name="audio-offset" type="d">
|
||||
<key name="audio-offset" type="i">
|
||||
<default>0</default>
|
||||
<summary>Offset time for audio tracks relative to video (milliseconds)</summary>
|
||||
</key>
|
||||
|
||||
<!-- Subtitles -->
|
||||
<key name="subtitle-offset" type="d">
|
||||
<key name="subtitle-offset" type="i">
|
||||
<default>0</default>
|
||||
<summary>Offset time for subtitle tracks relative to video (milliseconds)</summary>
|
||||
</key>
|
||||
@@ -95,7 +91,7 @@
|
||||
|
||||
<!-- GStreamer -->
|
||||
<key name="plugin-ranking" type="s">
|
||||
<default>'[{"apply":false,"name":"vah264dec","rank":300}]'</default>
|
||||
<default>'{}'</default>
|
||||
<summary>Custom values for GStreamer plugin ranking</summary>
|
||||
</key>
|
||||
<key name="play-flags" type="i">
|
||||
@@ -108,8 +104,8 @@
|
||||
<default>false</default>
|
||||
<summary>Enable to use adaptive streaming for YouTube</summary>
|
||||
</key>
|
||||
<key name="yt-quality-type" type="s">
|
||||
<default>"hfr"</default>
|
||||
<key name="yt-quality-type" type="i">
|
||||
<default>1</default>
|
||||
<summary>Max YouTube video quality type</summary>
|
||||
</key>
|
||||
|
||||
|
@@ -9,18 +9,18 @@
|
||||
<launchable type="desktop-id">com.github.rafostar.Clapper.desktop</launchable>
|
||||
<description>
|
||||
<p>
|
||||
Clapper is a GNOME media player build using GJS with GTK4 toolkit.
|
||||
Clapper is a GNOME media player built using GJS with GTK4 toolkit.
|
||||
The media player is using GStreamer as a media backend and renders
|
||||
everything via OpenGL. Player works natively on both Xorg and Wayland.
|
||||
It also supports hardware acceleration through VA-API on AMD/Intel GPUs,
|
||||
NVDEC for Nvidia and V4L2 for mobile devices.
|
||||
NVDEC on Nvidia and V4L2 on mobile devices.
|
||||
</p>
|
||||
<p>
|
||||
The media player has an adaptive GUI. When viewing videos in "Windowed Mode",
|
||||
Clapper will use mostly unmodified GTK widgets to match your OS look nicely.
|
||||
When player enters "Fullscreen Mode" all GUI elements will become darker, bigger
|
||||
and semi-transparent for your viewing comfort. It also has a "Floating Mode" which
|
||||
displays video only on top of all other windows for a PiP-like viewing experience.
|
||||
displays only video on top of all other windows for a PiP-like viewing experience.
|
||||
Mobile friendly transitions are also supported.
|
||||
</p>
|
||||
</description>
|
||||
@@ -118,6 +118,14 @@
|
||||
</release>
|
||||
</releases>
|
||||
<content_rating type="oars-1.1" />
|
||||
<recommends>
|
||||
<control>keyboard</control>
|
||||
<control>pointing</control>
|
||||
<control>touch</control>
|
||||
</recommends>
|
||||
<requires>
|
||||
<display_length compare="ge">small</display_length>
|
||||
</requires>
|
||||
<custom>
|
||||
<value key="Purism::form_factor">workstation</value>
|
||||
<value key="Purism::form_factor">mobile</value>
|
||||
|
@@ -1,30 +0,0 @@
|
||||
pkgbase = clapper-git
|
||||
pkgdesc = A GNOME media player built using GJS with GTK4 toolkit and powered by GStreamer with OpenGL rendering.
|
||||
pkgver = r745.4738673
|
||||
pkgrel = 1
|
||||
url = https://github.com/Rafostar/clapper
|
||||
arch = any
|
||||
license = GPL-3.0
|
||||
makedepends = meson>=0.50
|
||||
makedepends = git
|
||||
makedepends = gobject-introspection
|
||||
depends = gtk4
|
||||
depends = gjs
|
||||
depends = glib2>=2.56.0
|
||||
depends = wayland-protocols
|
||||
depends = hicolor-icon-theme
|
||||
depends = gstreamer>=1.18.0
|
||||
depends = gst-plugins-base>=1.18.0
|
||||
depends = gst-plugins-good>=1.18.0
|
||||
depends = gst-plugins-bad>=1.18.0
|
||||
optdepends = gst-libav>=1.18.0: Popular video decoders
|
||||
optdepends = gstreamer-vaapi>=1.18.0: Intel/AMD video acceleration
|
||||
optdepends = gst-plugins-ugly>=1.18.0: CD/DVD playback
|
||||
provides = clapper
|
||||
provides = libgstclapper-1.0
|
||||
conflicts = clapper
|
||||
source = clapper::git+https://github.com/Rafostar/clapper.git
|
||||
md5sums = SKIP
|
||||
|
||||
pkgname = clapper-git
|
||||
|
4
pkgs/arch/.gitignore
vendored
4
pkgs/arch/.gitignore
vendored
@@ -1,4 +0,0 @@
|
||||
clapper-*/
|
||||
clapper-*.pkg.tar.*
|
||||
pkg/
|
||||
src/
|
@@ -1,74 +0,0 @@
|
||||
#
|
||||
# PKGBUILD file for package clapper
|
||||
#
|
||||
# Copyright (C) 2020/21 sp1rit
|
||||
# Copyright (C) 2020 Rafostar
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# Maintainer: sp1rit <sp1ritCS@protonmail.com>
|
||||
|
||||
_basename=clapper
|
||||
pkgname="${_basename}-git"
|
||||
pkgver=r745.4738673
|
||||
pkgrel=1
|
||||
pkgdesc="A GNOME media player built using GJS with GTK4 toolkit and powered by GStreamer with OpenGL rendering."
|
||||
arch=(any)
|
||||
url="https://github.com/Rafostar/clapper"
|
||||
license=("GPL-3.0")
|
||||
depends=(
|
||||
"gtk4"
|
||||
"gjs"
|
||||
"glib2>=2.56.0" # glib-2.0, gmodule-2.0, gio-2.0
|
||||
"wayland-protocols" # gtk4 non-default runtime dep
|
||||
"hicolor-icon-theme"
|
||||
"gstreamer>=1.18.0" # gstreamer-1.0, gstreamer-base-1.0
|
||||
"gst-plugins-base>=1.18.0"
|
||||
"gst-plugins-good>=1.18.0"
|
||||
"gst-plugins-bad>=1.18.0"
|
||||
)
|
||||
makedepends=(
|
||||
"meson>=0.50"
|
||||
"git"
|
||||
"gobject-introspection" # /usr/sbin/g-ir-scanner
|
||||
)
|
||||
optdepends=(
|
||||
"gst-libav>=1.18.0: Popular video decoders"
|
||||
"gstreamer-vaapi>=1.18.0: Intel/AMD video acceleration"
|
||||
"gst-plugins-ugly>=1.18.0: CD/DVD playback"
|
||||
)
|
||||
source=("${_basename}::git+https://github.com/Rafostar/${_basename}.git")
|
||||
provides=("${_basename}" "libgst${_basename}-1.0")
|
||||
conflicts=("${_basename}")
|
||||
md5sums=("SKIP")
|
||||
|
||||
pkgver() {
|
||||
cd "${srcdir}/${_basename}"
|
||||
printf "r%s.%s" "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)"
|
||||
}
|
||||
|
||||
prepare() {
|
||||
cd "${srcdir}/${_basename}"
|
||||
arch-meson . _build
|
||||
}
|
||||
|
||||
build() {
|
||||
cd "${srcdir}/${_basename}"
|
||||
ninja -C _build
|
||||
}
|
||||
|
||||
package() {
|
||||
cd "${srcdir}/${_basename}"
|
||||
DESTDIR="$pkgdir" meson install -C _build/
|
||||
}
|
@@ -1,28 +0,0 @@
|
||||
Format: 3.0 (quilt)
|
||||
Source: clapper
|
||||
Binary: clapper
|
||||
Architecture: any
|
||||
Version: 0.3.0
|
||||
Maintainer: Rafostar <rafostar.github@gmail.com>
|
||||
Build-Depends: debhelper (>= 10),
|
||||
meson (>= 0.50),
|
||||
gjs,
|
||||
gobject-introspection,
|
||||
libgtk-4-dev (>= 4.0.0),
|
||||
libgstreamer1.0-dev (>= 1.18),
|
||||
libgstreamer-plugins-base1.0-dev (>= 1.18),
|
||||
libgstreamer-gl1.0-0 (>= 1.18),
|
||||
libgles-dev,
|
||||
libglib2.0-dev,
|
||||
libglib2.0-bin,
|
||||
desktop-file-utils,
|
||||
hicolor-icon-theme,
|
||||
brz,
|
||||
libfontconfig1-dev,
|
||||
libpam-systemd
|
||||
Package-List:
|
||||
clapper deb gnome optional arch=any
|
||||
Files:
|
||||
0 0 debian.tar.xz
|
||||
Description: Simple and modern GNOME media player
|
||||
A GNOME media player built using GJS with GTK4 toolkit and powered by GStreamer with OpenGL rendering.
|
@@ -1,5 +0,0 @@
|
||||
clapper (0.3.0) unstable; urgency=low
|
||||
|
||||
* New version
|
||||
|
||||
-- Rafostar <rafostar.github@gmail.com> Fri, 18 Jun 2021 09:39:00 +0100
|
@@ -1 +0,0 @@
|
||||
10
|
@@ -1,37 +0,0 @@
|
||||
Source: clapper
|
||||
Section: gnome
|
||||
Priority: optional
|
||||
Maintainer: Rafostar <rafostar.github@gmail.com>
|
||||
Standards-Version: 4.4.0
|
||||
Build-Depends: debhelper (>= 10),
|
||||
meson (>= 0.50),
|
||||
gjs,
|
||||
gobject-introspection,
|
||||
libgtk-4-dev (>= 4.0.0),
|
||||
libgstreamer1.0-dev (>= 1.18),
|
||||
libgstreamer-plugins-base1.0-dev (>= 1.18),
|
||||
libgstreamer-gl1.0-0 (>= 1.18),
|
||||
libgles-dev,
|
||||
libglib2.0-dev,
|
||||
libglib2.0-bin,
|
||||
desktop-file-utils,
|
||||
hicolor-icon-theme
|
||||
|
||||
Package: clapper
|
||||
Architecture: any
|
||||
Depends: gjs,
|
||||
gir1.2-gtk-4.0 (>= 4.0.0),
|
||||
hicolor-icon-theme,
|
||||
libgstreamer1.0-0 (>= 1.18),
|
||||
gstreamer1.0-plugins-base (>= 1.18),
|
||||
gstreamer1.0-plugins-good (>= 1.18),
|
||||
gstreamer1.0-plugins-bad (>= 1.18),
|
||||
gstreamer1.0-gl (>= 1.18)
|
||||
Recommends: gstreamer1.0-libav,
|
||||
gstreamer1.0-pulseaudio
|
||||
Suggests: gstreamer1.0-plugins-ugly,
|
||||
gstreamer1.0-vaapi
|
||||
Description: Simple and modern GNOME media player
|
||||
A GNOME media player built using GJS with GTK4 toolkit and powered by GStreamer with OpenGL rendering.
|
||||
.
|
||||
More codecs/features and video acceleration can be enabled by installing the suggested packages.
|
@@ -1,27 +0,0 @@
|
||||
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: clapper
|
||||
Source: https://github.com/Rafostar/clapper
|
||||
|
||||
Files: *
|
||||
Copyright: 2020 Rafostar <rafostar.github@gmail.com>
|
||||
License: GPL-3.0+
|
||||
|
||||
Files: debian/*
|
||||
Copyright: 2020 Rafostar <rafostar.github@gmail.com>
|
||||
License: GPL-3.0+
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
.
|
||||
On Debian systems, the complete text of the GNU General Public License
|
||||
Version 3 can be found in `/usr/share/common-licenses/GPL-3'.
|
@@ -1,4 +0,0 @@
|
||||
#!/usr/bin/make -f
|
||||
|
||||
%:
|
||||
dh $@
|
@@ -1 +0,0 @@
|
||||
3.0 (quilt)
|
@@ -36,6 +36,7 @@
|
||||
"testing/gstreamer-1.0/gst-plugins-ugly.json",
|
||||
"testing/gstreamer-1.0/gst-libav.json",
|
||||
"testing/gstreamer-1.0/gstreamer-vaapi.json",
|
||||
"testing/libadwaita.json",
|
||||
{
|
||||
"name": "clapper",
|
||||
"buildsystem": "meson",
|
||||
|
@@ -42,6 +42,7 @@
|
||||
"flathub/gstreamer-1.0/gst-libav.json",
|
||||
"flathub/gstreamer-1.0/gstreamer-vaapi.json",
|
||||
"flathub/lib/gtk4.json",
|
||||
"testing/libadwaita.json",
|
||||
{
|
||||
"name": "clapper",
|
||||
"buildsystem": "meson",
|
||||
|
21
pkgs/flatpak/testing/libadwaita.json
Normal file
21
pkgs/flatpak/testing/libadwaita.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "libadwaita",
|
||||
"buildsystem": "meson",
|
||||
"config-opts": [
|
||||
"--buildtype=release",
|
||||
"--wrap-mode=nofallback",
|
||||
"-Dintrospection=enabled",
|
||||
"-Dvapi=false",
|
||||
"-Dgtk_doc=false",
|
||||
"-Dtests=false",
|
||||
"-Dexamples=false"
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://gitlab.gnome.org/GNOME/libadwaita.git",
|
||||
"tag": "1.0.0-alpha.2",
|
||||
"commit": "f5932ab4250c8e709958c6e75a1a4941a5f0f386"
|
||||
}
|
||||
]
|
||||
}
|
@@ -46,6 +46,7 @@ BuildRequires: hicolor-icon-theme
|
||||
|
||||
Requires: gjs
|
||||
Requires: gtk4 >= %{gtk4_version}
|
||||
Requires: libadwaita
|
||||
Requires: hicolor-icon-theme
|
||||
|
||||
%if 0%{?suse_version}
|
||||
@@ -126,6 +127,9 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop
|
||||
%{_libdir}/%{appname}/
|
||||
|
||||
%changelog
|
||||
* Mon Aug 23 2021 Rafostar <rafostar.github@gmail.com> - 0.3.0-2
|
||||
- Require libadwaita
|
||||
|
||||
* Fri Jun 18 2021 Rafostar <rafostar.github@gmail.com> - 0.3.0-1
|
||||
- New version
|
||||
|
||||
|
@@ -1,5 +1,6 @@
|
||||
const { Gtk } = imports.gi;
|
||||
const Dialogs = imports.src.dialogs;
|
||||
const Prefs = imports.src.prefs;
|
||||
const Misc = imports.src.misc;
|
||||
|
||||
var actions = {
|
||||
@@ -42,7 +43,7 @@ function handleAction(action, window)
|
||||
new Dialogs.UriDialog(window);
|
||||
break;
|
||||
case 'prefs':
|
||||
new Dialogs.PrefsDialog(window);
|
||||
new Prefs.PrefsWindow(window);
|
||||
break;
|
||||
case 'shortcuts':
|
||||
if(!window.get_help_overlay()) {
|
||||
|
@@ -29,9 +29,6 @@ class ClapperAppBase extends Gtk.Application
|
||||
title: Misc.appName,
|
||||
});
|
||||
|
||||
if(Gtk.MINOR_VERSION > 0 || Gtk.MICRO_VERSION > 1)
|
||||
window.add_css_class('gtk402');
|
||||
|
||||
if(!settings.get_boolean('render-shadows'))
|
||||
window.add_css_class('gpufriendly');
|
||||
|
||||
@@ -111,7 +108,6 @@ class ClapperAppBase extends Gtk.Application
|
||||
{
|
||||
const theme = gtkSettings.gtk_theme_name;
|
||||
const window = this.active_window;
|
||||
const hasAdwThemeDark = window.has_css_class('adwthemedark');
|
||||
|
||||
debug(`user selected theme: ${theme}`);
|
||||
|
||||
@@ -119,27 +115,6 @@ class ClapperAppBase extends Gtk.Application
|
||||
Having 2/4 corners rounded in floating mode is not good. */
|
||||
if(!window.has_css_class('adwrounded'))
|
||||
window.add_css_class('adwrounded');
|
||||
|
||||
if(theme.startsWith('Adwaita') || theme.startsWith('Default')) {
|
||||
const isDarkTheme = settings.get_boolean('dark-theme');
|
||||
|
||||
if(isDarkTheme && !hasAdwThemeDark)
|
||||
window.add_css_class('adwthemedark');
|
||||
else if(!isDarkTheme && hasAdwThemeDark)
|
||||
window.remove_css_class('adwthemedark');
|
||||
}
|
||||
else if(hasAdwThemeDark)
|
||||
window.remove_css_class('adwthemedark');
|
||||
|
||||
if(!theme.endsWith('-dark'))
|
||||
return;
|
||||
|
||||
/* We need to request a default theme with optional dark variant
|
||||
to make the "gtk_application_prefer_dark_theme" setting work */
|
||||
const parsedTheme = theme.substring(0, theme.lastIndexOf('-'));
|
||||
|
||||
gtkSettings.gtk_theme_name = parsedTheme;
|
||||
debug(`set theme: ${parsedTheme}`);
|
||||
}
|
||||
|
||||
_onIconThemeChanged(gtkSettings)
|
||||
|
17
src/controls.js
vendored
17
src/controls.js
vendored
@@ -435,12 +435,19 @@ class ClapperControls extends Gtk.Box
|
||||
if(isVisible) {
|
||||
const [start, end] = this.positionScale.get_slider_range();
|
||||
const controlsHeight = this.parent.get_height();
|
||||
const scaleHeight = this.positionScale.parent.get_height();
|
||||
const scaleBoxHeight = this.positionScale.parent.get_height();
|
||||
const [isShared, destX, destY] = this.positionScale.translate_coordinates(
|
||||
this.positionScale.parent, 0, 0
|
||||
);
|
||||
|
||||
/* Half of slider width, values are defined in CSS */
|
||||
const sliderOffset = (this.isFullscreen && !this.isMobile)
|
||||
? 10 : 9;
|
||||
|
||||
this.chapterPopover.set_pointing_to(new Gdk.Rectangle({
|
||||
x: -2,
|
||||
y: -(controlsHeight - scaleHeight) / 2,
|
||||
width: 2 * end,
|
||||
x: destX + end - sliderOffset,
|
||||
y: -(controlsHeight - scaleBoxHeight) / 2,
|
||||
width: 0,
|
||||
height: 0,
|
||||
}));
|
||||
}
|
||||
@@ -469,7 +476,7 @@ class ClapperControls extends Gtk.Box
|
||||
scrollController.connect('scroll', clapperWidget._onScroll.bind(clapperWidget));
|
||||
this.volumeButton.add_controller(scrollController);
|
||||
|
||||
const initialVolume = (settings.get_string('volume-initial') === 'custom')
|
||||
const initialVolume = (settings.get_boolean('volume-custom'))
|
||||
? settings.get_int('volume-value') / 100
|
||||
: settings.get_double('volume-last');
|
||||
|
||||
|
@@ -3,8 +3,6 @@ const System = imports.system;
|
||||
const Debug = imports.src.debug;
|
||||
const FileOps = imports.src.fileOps;
|
||||
const Misc = imports.src.misc;
|
||||
const Prefs = imports.src.prefs;
|
||||
const PrefsBase = imports.src.prefsBase;
|
||||
|
||||
const { debug } = Debug;
|
||||
|
||||
@@ -247,88 +245,6 @@ class ClapperResumeDialog extends Gtk.MessageDialog
|
||||
}
|
||||
});
|
||||
|
||||
var PrefsDialog = GObject.registerClass(
|
||||
class ClapperPrefsDialog extends Gtk.Dialog
|
||||
{
|
||||
_init(window)
|
||||
{
|
||||
super._init({
|
||||
transient_for: window,
|
||||
destroy_with_parent: true,
|
||||
modal: true,
|
||||
title: 'Preferences',
|
||||
default_width: 460,
|
||||
default_height: 400,
|
||||
});
|
||||
|
||||
const pages = [
|
||||
{
|
||||
title: 'Player',
|
||||
pages: [
|
||||
{
|
||||
title: 'General',
|
||||
widget: Prefs.GeneralPage,
|
||||
},
|
||||
{
|
||||
title: 'Behaviour',
|
||||
widget: Prefs.BehaviourPage,
|
||||
},
|
||||
{
|
||||
title: 'Audio',
|
||||
widget: Prefs.AudioPage,
|
||||
},
|
||||
{
|
||||
title: 'Subtitles',
|
||||
widget: Prefs.SubtitlesPage,
|
||||
},
|
||||
{
|
||||
title: 'Network',
|
||||
widget: Prefs.NetworkPage,
|
||||
},
|
||||
{
|
||||
title: 'YouTube',
|
||||
widget: Prefs.YouTubePage,
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Advanced',
|
||||
pages: [
|
||||
{
|
||||
title: 'GStreamer',
|
||||
widget: Prefs.GStreamerPage,
|
||||
},
|
||||
{
|
||||
title: 'Tweaks',
|
||||
widget: Prefs.TweaksPage,
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
const prefsNotebook = new PrefsBase.Notebook(pages);
|
||||
prefsNotebook.add_css_class('prefsnotebook');
|
||||
|
||||
const area = this.get_content_area();
|
||||
area.append(prefsNotebook);
|
||||
|
||||
this.closeSignal = this.connect('close-request', this._onCloseRequest.bind(this));
|
||||
this.show();
|
||||
}
|
||||
|
||||
_onCloseRequest(dialog)
|
||||
{
|
||||
debug('closing prefs dialog');
|
||||
|
||||
dialog.disconnect(this.closeSignal);
|
||||
this.closeSignal = null;
|
||||
|
||||
const area = dialog.get_content_area();
|
||||
const notebook = area.get_first_child();
|
||||
notebook._onClose();
|
||||
}
|
||||
});
|
||||
|
||||
var AboutDialog = GObject.registerClass(
|
||||
class ClapperAboutDialog extends Gtk.AboutDialog
|
||||
{
|
||||
|
@@ -17,7 +17,6 @@ class ClapperHeaderBarBase extends Gtk.Box
|
||||
margin_start: 6,
|
||||
margin_end: 6,
|
||||
});
|
||||
this.add_css_class('osd');
|
||||
this.add_css_class('osdheaderbar');
|
||||
|
||||
this.isMaximized = false;
|
||||
@@ -40,9 +39,10 @@ class ClapperHeaderBarBase extends Gtk.Box
|
||||
valign: Gtk.Align.CENTER,
|
||||
can_focus: false,
|
||||
});
|
||||
const menuToggleButton = this.menuButton.get_first_child();
|
||||
menuToggleButton.add_css_class('osd');
|
||||
const mainMenuModel = uiBuilder.get_object('mainMenu');
|
||||
const mainMenuPopover = new HeaderBarPopover(mainMenuModel);
|
||||
mainMenuPopover.add_css_class('menupopover');
|
||||
this.menuButton.set_popover(mainMenuPopover);
|
||||
this.menuButton.add_css_class('circular');
|
||||
this.menuWidget.append(this.menuButton);
|
||||
@@ -57,6 +57,7 @@ class ClapperHeaderBarBase extends Gtk.Box
|
||||
icon_name: 'go-bottom-symbolic',
|
||||
can_focus: false,
|
||||
});
|
||||
floatButton.add_css_class('osd');
|
||||
floatButton.add_css_class('circular');
|
||||
floatButton.add_css_class('linkedleft');
|
||||
floatButton.connect('clicked',
|
||||
@@ -74,6 +75,7 @@ class ClapperHeaderBarBase extends Gtk.Box
|
||||
icon_name: 'view-fullscreen-symbolic',
|
||||
can_focus: false,
|
||||
});
|
||||
fullscreenButton.add_css_class('osd');
|
||||
fullscreenButton.add_css_class('circular');
|
||||
fullscreenButton.add_css_class('linkedright');
|
||||
fullscreenButton.connect('clicked',
|
||||
@@ -202,6 +204,7 @@ class ClapperHeaderBarBase extends Gtk.Box
|
||||
valign: Gtk.Align.CENTER,
|
||||
can_focus: false,
|
||||
});
|
||||
button.add_css_class('osd');
|
||||
button.add_css_class('circular');
|
||||
|
||||
if(name === 'maximize')
|
||||
|
@@ -2,8 +2,11 @@ imports.gi.versions.Gdk = '4.0';
|
||||
imports.gi.versions.Gtk = '4.0';
|
||||
imports.gi.versions.Soup = '2.4';
|
||||
|
||||
const { Gst } = imports.gi;
|
||||
const { Gst, Gtk, Adw } = imports.gi;
|
||||
|
||||
Gst.init(null);
|
||||
Gtk.init();
|
||||
Adw.init();
|
||||
|
||||
const { App } = imports.src.app;
|
||||
|
||||
|
@@ -2,6 +2,11 @@ imports.gi.versions.Gdk = '4.0';
|
||||
imports.gi.versions.Gtk = '4.0';
|
||||
imports.gi.versions.Soup = '2.4';
|
||||
|
||||
const { Gtk, Adw } = imports.gi;
|
||||
|
||||
Gtk.init();
|
||||
Adw.init();
|
||||
|
||||
const { AppRemote } = imports.src.appRemote;
|
||||
const Misc = imports.src.misc;
|
||||
|
||||
|
@@ -44,7 +44,7 @@ class ClapperPlayer extends GstClapper.Clapper
|
||||
this.ytClient = null;
|
||||
this.playlistWidget = new PlaylistWidget();
|
||||
|
||||
this.seek_done = true;
|
||||
this.seekDone = true;
|
||||
this.needsFastSeekRestore = false;
|
||||
this.customVideoTitle = null;
|
||||
|
||||
@@ -70,6 +70,7 @@ class ClapperPlayer extends GstClapper.Clapper
|
||||
set_and_bind_settings()
|
||||
{
|
||||
const settingsToSet = [
|
||||
'after-playback',
|
||||
'seeking-mode',
|
||||
'audio-offset',
|
||||
'subtitle-offset',
|
||||
@@ -81,7 +82,6 @@ class ClapperPlayer extends GstClapper.Clapper
|
||||
this._onSettingsKeyChanged(settings, key);
|
||||
|
||||
const flag = Gio.SettingsBindFlags.GET;
|
||||
settings.bind('keep-last-frame', this.widget, 'keep-last-frame', flag);
|
||||
settings.bind('subtitle-font', this.pipeline, 'subtitle-font-desc', flag);
|
||||
}
|
||||
|
||||
@@ -96,25 +96,23 @@ class ClapperPlayer extends GstClapper.Clapper
|
||||
|
||||
set_all_plugins_ranks()
|
||||
{
|
||||
let data = [];
|
||||
let data = {};
|
||||
|
||||
/* Set empty plugin list if someone messed it externally */
|
||||
try {
|
||||
data = JSON.parse(settings.get_string('plugin-ranking'));
|
||||
if(!Array.isArray(data))
|
||||
throw new Error('plugin ranking data is not an array!');
|
||||
if(Array.isArray(data)) {
|
||||
data = {};
|
||||
throw new Error('plugin ranking data is not an object');
|
||||
}
|
||||
}
|
||||
catch(err) {
|
||||
debug(err);
|
||||
settings.set_string('plugin-ranking', "[]");
|
||||
settings.set_string('plugin-ranking', "{}");
|
||||
}
|
||||
|
||||
for(let plugin of data) {
|
||||
if(!plugin.apply || !plugin.name)
|
||||
continue;
|
||||
|
||||
this.set_plugin_rank(plugin.name, plugin.rank);
|
||||
}
|
||||
for(let plugin of Object.keys(data))
|
||||
this.set_plugin_rank(plugin, data[plugin]);
|
||||
}
|
||||
|
||||
set_plugin_rank(name, rank)
|
||||
@@ -284,7 +282,7 @@ class ClapperPlayer extends GstClapper.Clapper
|
||||
if(this.needsTocUpdate)
|
||||
return;
|
||||
|
||||
this.seek_done = false;
|
||||
this.seekDone = false;
|
||||
|
||||
if(this.state === GstClapper.ClapperState.STOPPED)
|
||||
this.pause();
|
||||
@@ -292,8 +290,6 @@ class ClapperPlayer extends GstClapper.Clapper
|
||||
if(position < 0)
|
||||
position = 0;
|
||||
|
||||
debug(`${this.seekingMode} seeking to position: ${position}`);
|
||||
|
||||
super.seek(position);
|
||||
}
|
||||
|
||||
@@ -304,13 +300,12 @@ class ClapperPlayer extends GstClapper.Clapper
|
||||
|
||||
seek_chapter(seconds)
|
||||
{
|
||||
if(this.seekingMode !== 'fast') {
|
||||
if(this.seek_mode !== GstClapper.ClapperSeekMode.FAST) {
|
||||
this.seek_seconds(seconds);
|
||||
return;
|
||||
}
|
||||
|
||||
this.set_seek_mode(GstClapper.ClapperSeekMode.DEFAULT);
|
||||
this.seekingMode = 'normal';
|
||||
this.needsFastSeekRestore = true;
|
||||
|
||||
this.seek_seconds(seconds);
|
||||
@@ -318,20 +313,19 @@ class ClapperPlayer extends GstClapper.Clapper
|
||||
|
||||
adjust_position(isIncrease)
|
||||
{
|
||||
this.seek_done = false;
|
||||
this.seekDone = false;
|
||||
|
||||
const { controls } = this.widget.get_ancestor(Gtk.Grid);
|
||||
const max = controls.positionAdjustment.get_upper();
|
||||
const seekingUnit = settings.get_string('seeking-unit');
|
||||
|
||||
let seekingValue = settings.get_int('seeking-value');
|
||||
|
||||
switch(seekingUnit) {
|
||||
case 'minute':
|
||||
seekingValue *= 60;
|
||||
switch(settings.get_int('seeking-unit')) {
|
||||
case 2: /* Percentage */
|
||||
seekingValue *= max / 100;
|
||||
break;
|
||||
case 'percentage':
|
||||
seekingValue = max * seekingValue / 100;
|
||||
case 1: /* Minute */
|
||||
seekingValue *= 60;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -538,16 +532,15 @@ class ClapperPlayer extends GstClapper.Clapper
|
||||
const clapperWidget = player.widget.get_ancestor(Gtk.Grid);
|
||||
if(!clapperWidget) return;
|
||||
|
||||
if(!this.seek_done && state !== GstClapper.ClapperState.BUFFERING) {
|
||||
if(!this.seekDone && state !== GstClapper.ClapperState.BUFFERING) {
|
||||
clapperWidget.updateTime();
|
||||
|
||||
if(this.needsFastSeekRestore) {
|
||||
this.set_seek_mode(GstClapper.ClapperSeekMode.FAST);
|
||||
this.seekingMode = 'fast';
|
||||
this.needsFastSeekRestore = false;
|
||||
}
|
||||
|
||||
this.seek_done = true;
|
||||
this.seekDone = true;
|
||||
debug('seeking finished');
|
||||
|
||||
clapperWidget._onPlayerPositionUpdated(this, this.position);
|
||||
@@ -566,7 +559,8 @@ class ClapperPlayer extends GstClapper.Clapper
|
||||
if(this.playlistWidget._handleStreamEnded(player))
|
||||
return;
|
||||
|
||||
if(settings.get_boolean('close-auto')) {
|
||||
/* After playback equal 2 means close the app */
|
||||
if(settings.get_int('after-playback') === 2) {
|
||||
/* Stop will be automatically called soon afterwards */
|
||||
this.quitOnStop = true;
|
||||
this._performCloseCleanup(this.widget.get_root());
|
||||
@@ -640,16 +634,18 @@ class ClapperPlayer extends GstClapper.Clapper
|
||||
let root, value, action;
|
||||
|
||||
switch(key) {
|
||||
case 'after-playback':
|
||||
this.widget.keep_last_frame = (settings.get_int(key) === 1);
|
||||
break;
|
||||
case 'seeking-mode':
|
||||
this.seekingMode = settings.get_string('seeking-mode');
|
||||
switch(this.seekingMode) {
|
||||
case 'fast':
|
||||
switch(settings.get_int(key)) {
|
||||
case 2: /* Fast */
|
||||
this.set_seek_mode(GstClapper.ClapperSeekMode.FAST);
|
||||
break;
|
||||
case 'accurate':
|
||||
case 1: /* Accurate */
|
||||
this.set_seek_mode(GstClapper.ClapperSeekMode.ACCURATE);
|
||||
break;
|
||||
default:
|
||||
default: /* Normal */
|
||||
this.set_seek_mode(GstClapper.ClapperSeekMode.DEFAULT);
|
||||
break;
|
||||
}
|
||||
@@ -669,12 +665,12 @@ class ClapperPlayer extends GstClapper.Clapper
|
||||
root[action + '_css_class'](gpuClass);
|
||||
break;
|
||||
case 'audio-offset':
|
||||
value = Math.round(settings.get_double(key) * -Gst.MSECOND);
|
||||
value = Math.round(settings.get_int(key) * -Gst.MSECOND);
|
||||
this.set_audio_video_offset(value);
|
||||
debug(`set audio-video offset: ${value}`);
|
||||
break;
|
||||
case 'subtitle-offset':
|
||||
value = Math.round(settings.get_double(key) * -Gst.MSECOND);
|
||||
value = Math.round(settings.get_int(key) * -Gst.MSECOND);
|
||||
this.set_subtitle_video_offset(value);
|
||||
debug(`set subtitle-video offset: ${value}`);
|
||||
break;
|
||||
|
@@ -28,6 +28,7 @@ class ClapperPlaylistWidget extends Gtk.ListBox
|
||||
});
|
||||
this.activeRowId = -1;
|
||||
this.repeatMode = RepeatMode.NONE;
|
||||
this.add_css_class('playlist');
|
||||
|
||||
this.connect('row-activated', this._onRowActivated.bind(this));
|
||||
}
|
||||
@@ -254,6 +255,7 @@ class ClapperPlaylistItem extends Gtk.ListBoxRow
|
||||
}
|
||||
this.filename = filename || uri;
|
||||
this.set_tooltip_text(this.filename);
|
||||
this.add_css_class('playlistrow');
|
||||
|
||||
const box = new Gtk.Box({
|
||||
orientation: Gtk.Orientation.HORIZONTAL,
|
||||
|
763
src/prefs.js
763
src/prefs.js
@@ -1,354 +1,475 @@
|
||||
const { GObject, Gst, Gtk, Pango } = imports.gi;
|
||||
const { Adw, GObject, Gio, Gst, Gtk } = imports.gi;
|
||||
const Debug = imports.src.debug;
|
||||
const Misc = imports.src.misc;
|
||||
const PrefsBase = imports.src.prefsBase;
|
||||
|
||||
const { debug } = Debug;
|
||||
const { settings } = Misc;
|
||||
|
||||
/* PlayFlags are not exported through GI */
|
||||
Gst.PlayFlags = {
|
||||
VIDEO: 1,
|
||||
AUDIO: 2,
|
||||
TEXT: 4,
|
||||
VIS: 8,
|
||||
SOFT_VOLUME: 16,
|
||||
NATIVE_AUDIO: 32,
|
||||
NATIVE_VIDEO: 64,
|
||||
DOWNLOAD: 128,
|
||||
BUFFERING: 256,
|
||||
DEINTERLACE: 512,
|
||||
SOFT_COLORBALANCE: 1024,
|
||||
FORCE_FILTERS: 2048,
|
||||
FORCE_SW_DECODERS: 4096,
|
||||
VIDEO: 1,
|
||||
AUDIO: 2,
|
||||
TEXT: 4,
|
||||
VIS: 8,
|
||||
SOFT_VOLUME: 16,
|
||||
NATIVE_AUDIO: 32,
|
||||
NATIVE_VIDEO: 64,
|
||||
DOWNLOAD: 128,
|
||||
BUFFERING: 256,
|
||||
DEINTERLACE: 512,
|
||||
SOFT_COLORBALANCE: 1024,
|
||||
FORCE_FILTERS: 2048,
|
||||
FORCE_SW_DECODERS: 4096,
|
||||
};
|
||||
|
||||
var GeneralPage = GObject.registerClass(
|
||||
class ClapperGeneralPage extends PrefsBase.Grid
|
||||
const widgetOpts = {
|
||||
halign: Gtk.Align.CENTER,
|
||||
valign: Gtk.Align.CENTER,
|
||||
};
|
||||
|
||||
function getCommonProps()
|
||||
{
|
||||
_init()
|
||||
return {
|
||||
'schema-name': GObject.ParamSpec.string(
|
||||
'schema-name',
|
||||
'GSchema setting name',
|
||||
'Name of the setting to bind',
|
||||
GObject.ParamFlags.WRITABLE,
|
||||
null
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
const flags = Gio.SettingsBindFlags.DEFAULT;
|
||||
|
||||
let PrefsActionRow = GObject.registerClass({
|
||||
GTypeName: 'ClapperPrefsActionRow',
|
||||
Properties: getCommonProps(),
|
||||
},
|
||||
class ClapperPrefsActionRow extends Adw.ActionRow
|
||||
{
|
||||
_init(widget)
|
||||
{
|
||||
super._init();
|
||||
|
||||
this.addTitle('Startup');
|
||||
this.addCheckButton('Auto enter fullscreen', 'fullscreen-auto');
|
||||
this._schemaName = null;
|
||||
this._bindProp = null;
|
||||
|
||||
this.addTitle('Volume');
|
||||
const comboBox = this.addComboBoxText('Initial value', [
|
||||
['restore', "Restore"],
|
||||
['custom', "Custom"],
|
||||
], 'volume-initial');
|
||||
const spinButton = this.addSpinButton('Value (percentage)', 0, 200, 'volume-value');
|
||||
this._onVolumeInitialChanged(spinButton, comboBox);
|
||||
comboBox.connect('changed', this._onVolumeInitialChanged.bind(this, spinButton));
|
||||
|
||||
this.addTitle('Finish');
|
||||
this.addCheckButton('Keep showing last frame', 'keep-last-frame');
|
||||
this.addCheckButton('Close after playback', 'close-auto');
|
||||
this.add_suffix(widget);
|
||||
this.set_activatable_widget(widget);
|
||||
}
|
||||
|
||||
_onVolumeInitialChanged(spinButton, comboBox)
|
||||
set schema_name(value)
|
||||
{
|
||||
const value = comboBox.get_active_id();
|
||||
spinButton.set_visible(value === 'custom');
|
||||
}
|
||||
});
|
||||
|
||||
var BehaviourPage = GObject.registerClass(
|
||||
class ClapperBehaviourPage extends PrefsBase.Grid
|
||||
{
|
||||
_init()
|
||||
{
|
||||
super._init();
|
||||
|
||||
this.addTitle('Seeking');
|
||||
this.addComboBoxText('Mode', [
|
||||
['normal', "Normal"],
|
||||
['accurate', "Accurate"],
|
||||
['fast', "Fast"],
|
||||
], 'seeking-mode');
|
||||
this.addComboBoxText('Unit', [
|
||||
['second', "Second"],
|
||||
['minute', "Minute"],
|
||||
['percentage', "Percentage"],
|
||||
], 'seeking-unit');
|
||||
this.addSpinButton('Value', 1, 99, 'seeking-value');
|
||||
|
||||
this.addTitle('Resume');
|
||||
this.addCheckButton('Ask to resume last unfinished video', 'resume-enabled');
|
||||
|
||||
this.addTitle('Floating Mode');
|
||||
this.addCheckButton('Show on all workspaces', 'floating-stick');
|
||||
}
|
||||
});
|
||||
|
||||
var AudioPage = GObject.registerClass(
|
||||
class ClapperAudioPage extends PrefsBase.Grid
|
||||
{
|
||||
_init()
|
||||
{
|
||||
super._init();
|
||||
|
||||
this.addTitle('Synchronization');
|
||||
this.addSpinButton('Offset (milliseconds)', -1000, 1000, 'audio-offset', 25);
|
||||
|
||||
this.addTitle('Processing');
|
||||
this.addPlayFlagCheckButton('Only use native audio formats', Gst.PlayFlags.NATIVE_AUDIO);
|
||||
}
|
||||
});
|
||||
|
||||
var SubtitlesPage = GObject.registerClass(
|
||||
class ClapperSubtitlesPage extends PrefsBase.Grid
|
||||
{
|
||||
_init()
|
||||
{
|
||||
super._init();
|
||||
|
||||
/* FIXME: This should be moved to subtitles popup and displayed only when
|
||||
external subtitles were added for easier customization per video. */
|
||||
//this.addTitle('Synchronization');
|
||||
//this.addSpinButton('Offset (milliseconds)', -5000, 5000, 'subtitle-offset', 25);
|
||||
|
||||
this.addTitle('External Subtitles');
|
||||
this.addFontButton('Default font', 'subtitle-font');
|
||||
}
|
||||
});
|
||||
|
||||
var NetworkPage = GObject.registerClass(
|
||||
class ClapperNetworkPage extends PrefsBase.Grid
|
||||
{
|
||||
_init()
|
||||
{
|
||||
super._init();
|
||||
|
||||
this.addTitle('Client');
|
||||
this.addPlayFlagCheckButton('Progressive download buffering', Gst.PlayFlags.DOWNLOAD);
|
||||
|
||||
this.addTitle('Server');
|
||||
const webServer = this.addCheckButton('Control player remotely', 'webserver-enabled');
|
||||
const serverPort = this.addSpinButton('Listening port', 1024, 65535, 'webserver-port');
|
||||
webServer.bind_property('active', serverPort, 'visible', GObject.BindingFlags.SYNC_CREATE);
|
||||
const webApp = this.addCheckButton('Start built-in web application', 'webapp-enabled');
|
||||
webServer.bind_property('active', webApp, 'visible', GObject.BindingFlags.SYNC_CREATE);
|
||||
const webAppPort = this.addSpinButton('Web application port', 1024, 65535, 'webapp-port');
|
||||
webServer.bind_property('active', webAppPort, 'visible', GObject.BindingFlags.SYNC_CREATE);
|
||||
}
|
||||
});
|
||||
|
||||
var YouTubePage = GObject.registerClass(
|
||||
class ClapperYouTubePage extends PrefsBase.Grid
|
||||
{
|
||||
_init()
|
||||
{
|
||||
super._init();
|
||||
|
||||
this.addTitle('YouTube');
|
||||
this.addCheckButton('Prefer adaptive streaming', 'yt-adaptive-enabled');
|
||||
this.addComboBoxText('Max quality', [
|
||||
['normal', "Normal"],
|
||||
['hfr', "HFR"],
|
||||
], 'yt-quality-type');
|
||||
}
|
||||
});
|
||||
|
||||
var GStreamerPage = GObject.registerClass(
|
||||
class ClapperGStreamerPage extends PrefsBase.Grid
|
||||
{
|
||||
_init()
|
||||
{
|
||||
super._init();
|
||||
|
||||
this.addTitle('Plugin Ranking');
|
||||
const listStore = new Gtk.ListStore();
|
||||
listStore.set_column_types([
|
||||
GObject.TYPE_BOOLEAN,
|
||||
GObject.TYPE_STRING,
|
||||
GObject.TYPE_STRING,
|
||||
]);
|
||||
const treeView = new Gtk.TreeView({
|
||||
hexpand: true,
|
||||
vexpand: true,
|
||||
enable_search: false,
|
||||
model: listStore,
|
||||
});
|
||||
const treeSelection = treeView.get_selection();
|
||||
|
||||
const apply = new Gtk.TreeViewColumn({
|
||||
title: "Apply",
|
||||
});
|
||||
const name = new Gtk.TreeViewColumn({
|
||||
title: "Plugin",
|
||||
expand: true,
|
||||
});
|
||||
const rank = new Gtk.TreeViewColumn({
|
||||
title: "Rank",
|
||||
min_width: 90,
|
||||
});
|
||||
|
||||
const applyCell = new Gtk.CellRendererToggle();
|
||||
const nameCell = new Gtk.CellRendererText({
|
||||
editable: true,
|
||||
placeholder_text: "Insert plugin name",
|
||||
});
|
||||
const rankCell = new Gtk.CellRendererText({
|
||||
editable: true,
|
||||
weight: Pango.Weight.BOLD,
|
||||
placeholder_text: "Insert plugin rank",
|
||||
});
|
||||
|
||||
apply.pack_start(applyCell, true);
|
||||
name.pack_start(nameCell, true);
|
||||
rank.pack_start(rankCell, true);
|
||||
|
||||
apply.add_attribute(applyCell, 'active', 0);
|
||||
name.add_attribute(nameCell, 'text', 1);
|
||||
rank.add_attribute(rankCell, 'text', 2);
|
||||
|
||||
treeView.insert_column(apply, 0);
|
||||
treeView.insert_column(name, 1);
|
||||
treeView.insert_column(rank, 2);
|
||||
|
||||
const frame = new Gtk.Frame({
|
||||
child: treeView
|
||||
});
|
||||
this.addToGrid(frame);
|
||||
|
||||
const addButton = new Gtk.Button({
|
||||
icon_name: 'list-add-symbolic',
|
||||
halign: Gtk.Align.END,
|
||||
});
|
||||
const removeButton = new Gtk.Button({
|
||||
icon_name: 'list-remove-symbolic',
|
||||
sensitive: false,
|
||||
halign: Gtk.Align.END,
|
||||
});
|
||||
const label = new Gtk.Label({
|
||||
label: 'Changes require player restart',
|
||||
halign: Gtk.Align.START,
|
||||
hexpand: true,
|
||||
ellipsize: Pango.EllipsizeMode.END,
|
||||
});
|
||||
const box = new Gtk.Box({
|
||||
orientation: Gtk.Orientation.HORIZONTAL,
|
||||
spacing: 6,
|
||||
hexpand: true,
|
||||
});
|
||||
box.append(label);
|
||||
box.append(removeButton);
|
||||
box.append(addButton);
|
||||
this.addToGrid(box);
|
||||
|
||||
applyCell.connect('toggled', this._onApplyCellEdited.bind(this));
|
||||
nameCell.connect('edited', this._onNameCellEdited.bind(this));
|
||||
rankCell.connect('edited', this._onRankCellEdited.bind(this));
|
||||
addButton.connect('clicked', this._onAddButtonClicked.bind(this, listStore));
|
||||
removeButton.connect('clicked', this._onRemoveButtonClicked.bind(this, listStore));
|
||||
treeSelection.connect('changed', this._onTreeSelectionChanged.bind(this, removeButton));
|
||||
|
||||
this.settingsChangedSignal = settings.connect(
|
||||
'changed::plugin-ranking', this.refreshListStore.bind(this, listStore)
|
||||
);
|
||||
|
||||
this.refreshListStore(listStore);
|
||||
this._schemaName = value;
|
||||
}
|
||||
|
||||
refreshListStore(listStore)
|
||||
vfunc_realize()
|
||||
{
|
||||
const data = JSON.parse(settings.get_string('plugin-ranking'));
|
||||
listStore.clear();
|
||||
super.vfunc_realize();
|
||||
|
||||
for(let plugin of data) {
|
||||
listStore.set(
|
||||
listStore.append(),
|
||||
[0, 1, 2], [
|
||||
plugin.apply || false,
|
||||
plugin.name || '',
|
||||
plugin.rank || 0
|
||||
]
|
||||
if(this._schemaName && this._bindProp) {
|
||||
settings.bind(this._schemaName,
|
||||
this.activatable_widget, this._bindProp, flags
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
updatePlugin(index, prop, value)
|
||||
{
|
||||
const data = JSON.parse(settings.get_string('plugin-ranking'));
|
||||
data[index][prop] = value;
|
||||
settings.set_string('plugin-ranking', JSON.stringify(data));
|
||||
}
|
||||
|
||||
_onTreeSelectionChanged(removeButton, treeSelection)
|
||||
{
|
||||
const [isSelected, model, iter] = treeSelection.get_selected();
|
||||
this.activeIndex = -1;
|
||||
|
||||
if(isSelected) {
|
||||
this.activeIndex = Number(model.get_string_from_iter(iter));
|
||||
}
|
||||
|
||||
removeButton.set_sensitive(this.activeIndex >= 0);
|
||||
}
|
||||
|
||||
_onAddButtonClicked(listStore, button)
|
||||
{
|
||||
const data = JSON.parse(settings.get_string('plugin-ranking'));
|
||||
data.push({
|
||||
apply: false,
|
||||
name: '',
|
||||
rank: 0,
|
||||
});
|
||||
settings.set_string('plugin-ranking', JSON.stringify(data));
|
||||
}
|
||||
|
||||
_onRemoveButtonClicked(listStore, button)
|
||||
{
|
||||
if(this.activeIndex < 0)
|
||||
return;
|
||||
|
||||
const data = JSON.parse(settings.get_string('plugin-ranking'));
|
||||
data.splice(this.activeIndex, 1);
|
||||
settings.set_string('plugin-ranking', JSON.stringify(data));
|
||||
}
|
||||
|
||||
_onApplyCellEdited(cell, path)
|
||||
{
|
||||
const newState = !cell.active;
|
||||
this.updatePlugin(path, 'apply', newState);
|
||||
}
|
||||
|
||||
_onNameCellEdited(cell, path, newText)
|
||||
{
|
||||
newText = newText.trim();
|
||||
this.updatePlugin(path, 'name', newText);
|
||||
}
|
||||
|
||||
_onRankCellEdited(cell, path, newText)
|
||||
{
|
||||
newText = newText.trim();
|
||||
|
||||
if(isNaN(newText))
|
||||
newText = 0;
|
||||
|
||||
this.updatePlugin(path, 'rank', Number(newText));
|
||||
}
|
||||
|
||||
_onClose()
|
||||
{
|
||||
super._onClose('gstreamer');
|
||||
|
||||
settings.disconnect(this.settingsChangedSignal);
|
||||
this.settingsChangedSignal = null;
|
||||
this._schemaName = null;
|
||||
}
|
||||
});
|
||||
|
||||
var TweaksPage = GObject.registerClass(
|
||||
class ClapperTweaksPage extends PrefsBase.Grid
|
||||
let PrefsSubpageRow = GObject.registerClass({
|
||||
GTypeName: 'ClapperPrefsSubpageRow',
|
||||
Properties: getCommonProps(),
|
||||
},
|
||||
class ClapperPrefsSubpageRow extends Adw.ActionRow
|
||||
{
|
||||
_init(widget)
|
||||
{
|
||||
super._init({
|
||||
activatable: true,
|
||||
});
|
||||
|
||||
this._prefsSubpage = null;
|
||||
|
||||
const icon = new Gtk.Image({
|
||||
icon_name: 'go-next-symbolic',
|
||||
});
|
||||
this.add_suffix(icon);
|
||||
}
|
||||
|
||||
vfunc_activate()
|
||||
{
|
||||
super.vfunc_activate();
|
||||
|
||||
if(!this._prefsSubpage)
|
||||
this._prefsSubpage = this._createSubpage();
|
||||
|
||||
const prefs = this.get_ancestor(PrefsWindow);
|
||||
prefs.present_subpage(this._prefsSubpage);
|
||||
}
|
||||
|
||||
_createSubpage()
|
||||
{
|
||||
/* For override */
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
GObject.registerClass({
|
||||
GTypeName: 'ClapperPrefsSwitch',
|
||||
},
|
||||
class ClapperPrefsSwitch extends PrefsActionRow
|
||||
{
|
||||
_init()
|
||||
{
|
||||
super._init(new Gtk.Switch(widgetOpts));
|
||||
this._bindProp = 'active';
|
||||
}
|
||||
});
|
||||
|
||||
GObject.registerClass({
|
||||
GTypeName: 'ClapperPrefsPlayFlagSwitch',
|
||||
Properties: {
|
||||
'play-flag': GObject.ParamSpec.int(
|
||||
'play-flag',
|
||||
'PlayFlag',
|
||||
'Value of the gstreamer play flag to toggle',
|
||||
GObject.ParamFlags.WRITABLE,
|
||||
1, 4096, 1,
|
||||
),
|
||||
},
|
||||
},
|
||||
class ClapperPrefsPlayFlagSwitch extends PrefsActionRow
|
||||
{
|
||||
_init()
|
||||
{
|
||||
super._init(new Gtk.Switch(widgetOpts));
|
||||
|
||||
this._flag = 1;
|
||||
this._doneRealize = false;
|
||||
}
|
||||
|
||||
set play_flag(value)
|
||||
{
|
||||
this._flag = value;
|
||||
}
|
||||
|
||||
vfunc_realize()
|
||||
{
|
||||
super.vfunc_realize();
|
||||
|
||||
if(!this._doneRealize) {
|
||||
const playFlags = settings.get_int('play-flags');
|
||||
|
||||
this.activatable_widget.active = (
|
||||
(playFlags & this._flag) === this._flag
|
||||
);
|
||||
this.activatable_widget.connect(
|
||||
'notify::active', this._onPlayFlagToggled.bind(this)
|
||||
);
|
||||
}
|
||||
this._doneRealize = true;
|
||||
}
|
||||
|
||||
_onPlayFlagToggled()
|
||||
{
|
||||
let playFlags = settings.get_int('play-flags');
|
||||
|
||||
if(this.activatable_widget.active)
|
||||
playFlags |= this._flag;
|
||||
else
|
||||
playFlags &= ~this._flag;
|
||||
|
||||
settings.set_int('play-flags', playFlags);
|
||||
}
|
||||
});
|
||||
|
||||
GObject.registerClass({
|
||||
GTypeName: 'ClapperPrefsSpin',
|
||||
Properties: {
|
||||
'spin-adjustment': GObject.ParamSpec.object(
|
||||
'spin-adjustment',
|
||||
'GtkAdjustment',
|
||||
'Custom GtkAdjustment for spin button',
|
||||
GObject.ParamFlags.WRITABLE,
|
||||
Gtk.Adjustment
|
||||
),
|
||||
},
|
||||
},
|
||||
class ClapperPrefsSpin extends PrefsActionRow
|
||||
{
|
||||
_init()
|
||||
{
|
||||
super._init(new Gtk.SpinButton(widgetOpts));
|
||||
this._bindProp = 'value';
|
||||
}
|
||||
|
||||
set spin_adjustment(value)
|
||||
{
|
||||
this.activatable_widget.set_adjustment(value);
|
||||
}
|
||||
});
|
||||
|
||||
let PrefsPluginFeature = GObject.registerClass(
|
||||
class PrefsPluginFeature extends Adw.ActionRow
|
||||
{
|
||||
_init(featureObj)
|
||||
{
|
||||
super._init({
|
||||
title: featureObj.name,
|
||||
});
|
||||
|
||||
const enableSwitch = new Gtk.Switch(widgetOpts);
|
||||
const spinButton = new Gtk.SpinButton(widgetOpts);
|
||||
|
||||
spinButton.set_range(0, 512);
|
||||
spinButton.set_increments(1, 1);
|
||||
|
||||
enableSwitch.active = featureObj.enabled;
|
||||
spinButton.value = featureObj.rank;
|
||||
this.currentRank = featureObj.rank;
|
||||
|
||||
this.add_suffix(enableSwitch);
|
||||
this.add_suffix(spinButton);
|
||||
|
||||
enableSwitch.bind_property('active', spinButton, 'sensitive',
|
||||
GObject.BindingFlags.SYNC_CREATE
|
||||
);
|
||||
|
||||
enableSwitch.connect('notify::active', this._onSwitchActivate.bind(this));
|
||||
spinButton.connect('value-changed', this._onValueChanged.bind(this));
|
||||
}
|
||||
|
||||
_updateRanking(data)
|
||||
{
|
||||
settings.set_string('plugin-ranking', JSON.stringify(data));
|
||||
}
|
||||
|
||||
_onSwitchActivate(enableSwitch)
|
||||
{
|
||||
const { settingsData } = this.get_ancestor(PrefsPluginRankingSubpage);
|
||||
|
||||
if(enableSwitch.active)
|
||||
settingsData[this.title] = this.currentRank;
|
||||
else if(settingsData[this.title])
|
||||
delete settingsData[this.title];
|
||||
|
||||
this._updateRanking(settingsData);
|
||||
}
|
||||
|
||||
_onValueChanged(spinButton)
|
||||
{
|
||||
const { settingsData } = this.get_ancestor(PrefsPluginRankingSubpage);
|
||||
|
||||
this.currentRank = spinButton.value;
|
||||
settingsData[this.title] = this.currentRank;
|
||||
|
||||
this._updateRanking(settingsData);
|
||||
}
|
||||
});
|
||||
|
||||
GObject.registerClass({
|
||||
GTypeName: 'ClapperPrefsFont',
|
||||
},
|
||||
class ClapperPrefsFont extends PrefsActionRow
|
||||
{
|
||||
_init()
|
||||
{
|
||||
const opts = {
|
||||
use_font: true,
|
||||
use_size: true,
|
||||
};
|
||||
Object.assign(opts, widgetOpts);
|
||||
|
||||
super._init(new Gtk.FontButton(opts));
|
||||
this._bindProp = 'font';
|
||||
}
|
||||
});
|
||||
|
||||
GObject.registerClass({
|
||||
GTypeName: 'ClapperPrefsCombo',
|
||||
Properties: getCommonProps(),
|
||||
},
|
||||
class ClapperPrefsCombo extends Adw.ComboRow
|
||||
{
|
||||
_init()
|
||||
{
|
||||
super._init();
|
||||
this._schemaName = null;
|
||||
}
|
||||
|
||||
set schema_name(value)
|
||||
{
|
||||
this._schemaName = value;
|
||||
}
|
||||
|
||||
vfunc_realize()
|
||||
{
|
||||
super.vfunc_realize();
|
||||
|
||||
if(this._schemaName)
|
||||
settings.bind(this._schemaName, this, 'selected', flags);
|
||||
|
||||
this._schemaName = null;
|
||||
}
|
||||
});
|
||||
|
||||
GObject.registerClass({
|
||||
GTypeName: 'ClapperPrefsExpander',
|
||||
Properties: getCommonProps(),
|
||||
},
|
||||
class ClapperPrefsExpander extends Adw.ExpanderRow
|
||||
{
|
||||
_init()
|
||||
{
|
||||
super._init({
|
||||
show_enable_switch: true,
|
||||
});
|
||||
}
|
||||
|
||||
set schema_name(value)
|
||||
{
|
||||
settings.bind(value, this, 'enable-expansion', flags);
|
||||
}
|
||||
});
|
||||
|
||||
GObject.registerClass({
|
||||
GTypeName: 'ClapperPrefsPluginRankingSubpageRow',
|
||||
},
|
||||
class ClapperPrefsPluginRankingSubpageRow extends PrefsSubpageRow
|
||||
{
|
||||
_createSubpage()
|
||||
{
|
||||
return new PrefsPluginRankingSubpage();
|
||||
}
|
||||
});
|
||||
|
||||
let PrefsPluginExpander = GObject.registerClass(
|
||||
class ClapperPrefsPluginExpander extends Adw.ExpanderRow
|
||||
{
|
||||
_init(plugin)
|
||||
{
|
||||
super._init({
|
||||
title: plugin,
|
||||
show_enable_switch: false,
|
||||
});
|
||||
|
||||
this.expandSignal = this.connect(
|
||||
'notify::expanded', this._onExpandedNotify.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
_onExpandedNotify()
|
||||
{
|
||||
if(!this.expanded)
|
||||
return;
|
||||
|
||||
this.disconnect(this.expandSignal);
|
||||
this.expandSignal = null;
|
||||
|
||||
const { pluginsData } = this.get_ancestor(PrefsPluginRankingSubpage);
|
||||
|
||||
pluginsData[this.title].sort((a, b) =>
|
||||
(a.name > b.name) - (a.name < b.name)
|
||||
);
|
||||
const featuresNames = Object.keys(pluginsData[this.title]);
|
||||
debug(`Adding ${featuresNames.length} features to the list of plugin: ${this.title}`);
|
||||
|
||||
for(let featureObj of pluginsData[this.title])
|
||||
this.add(new PrefsPluginFeature(featureObj));
|
||||
}
|
||||
});
|
||||
|
||||
let PrefsPluginRankingSubpage = GObject.registerClass({
|
||||
GTypeName: 'ClapperPrefsPluginRankingSubpage',
|
||||
Template: `file://${Misc.getClapperPath()}/ui/preferences-plugin-ranking-subpage.ui`,
|
||||
InternalChildren: ['decoders_group'],
|
||||
},
|
||||
class ClapperPrefsPluginRankingSubpage extends Gtk.Box
|
||||
{
|
||||
_init()
|
||||
{
|
||||
super._init();
|
||||
|
||||
this.addTitle('Appearance');
|
||||
this.addCheckButton('Enable dark theme', 'dark-theme');
|
||||
if(!Gst.is_initialized())
|
||||
Gst.init(null);
|
||||
|
||||
this.addTitle('Performance');
|
||||
this.addCheckButton('Render window shadows', 'render-shadows');
|
||||
const gstRegistry = Gst.Registry.get();
|
||||
const decoders = gstRegistry.feature_filter(this._decodersFilterCb, false);
|
||||
|
||||
const plugins = {};
|
||||
this.settingsData = {};
|
||||
|
||||
/* In case someone messed up gsettings values */
|
||||
try {
|
||||
this.settingsData = JSON.parse(settings.get_string('plugin-ranking'));
|
||||
/* Might be an array in older Clapper versions */
|
||||
if(Array.isArray(this.settingsData))
|
||||
this.settingsData = {};
|
||||
}
|
||||
catch(err) { /* Ignore */ }
|
||||
|
||||
for(let decoder of decoders) {
|
||||
const pluginName = decoder.get_plugin_name();
|
||||
|
||||
/* Do not add unsupported plugins */
|
||||
switch(pluginName) {
|
||||
case 'playback':
|
||||
continue;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if(!plugins[pluginName])
|
||||
plugins[pluginName] = [];
|
||||
|
||||
const decName = decoder.get_name();
|
||||
|
||||
plugins[pluginName].push({
|
||||
name: decName,
|
||||
rank: decoder.get_rank(),
|
||||
enabled: this.settingsData[decName] != null,
|
||||
});
|
||||
}
|
||||
|
||||
const pluginsNames = Object.keys(plugins);
|
||||
debug(`Adding ${pluginsNames.length} found plugins to the list`);
|
||||
|
||||
this.pluginsData = pluginsNames.sort().reduce((res, key) =>
|
||||
(res[key] = plugins[key], res), {}
|
||||
);
|
||||
|
||||
for(let plugin in this.pluginsData)
|
||||
this._decoders_group.add(new PrefsPluginExpander(plugin));
|
||||
}
|
||||
|
||||
_decodersFilterCb(feature)
|
||||
{
|
||||
return (
|
||||
feature.list_is_type
|
||||
&& feature.list_is_type(Gst.ELEMENT_FACTORY_TYPE_DECODER)
|
||||
);
|
||||
}
|
||||
|
||||
_onReturnClicked(button)
|
||||
{
|
||||
const prefs = this.get_ancestor(PrefsWindow);
|
||||
prefs.close_subpage();
|
||||
}
|
||||
});
|
||||
|
||||
var PrefsWindow = GObject.registerClass({
|
||||
GTypeName: 'ClapperPrefsWindow',
|
||||
Template: `file://${Misc.getClapperPath()}/ui/preferences-window.ui`,
|
||||
},
|
||||
class ClapperPrefsWindow extends Adw.PreferencesWindow
|
||||
{
|
||||
_init(window)
|
||||
{
|
||||
super._init({
|
||||
transient_for: window,
|
||||
});
|
||||
|
||||
this.show();
|
||||
}
|
||||
});
|
||||
|
238
src/prefsBase.js
238
src/prefsBase.js
@@ -1,238 +0,0 @@
|
||||
const { Gio, GObject, Gtk } = imports.gi;
|
||||
const Debug = imports.src.debug;
|
||||
const Misc = imports.src.misc;
|
||||
|
||||
const { debug } = Debug;
|
||||
const { settings } = Misc;
|
||||
|
||||
var Notebook = GObject.registerClass(
|
||||
class ClapperPrefsNotebook extends Gtk.Notebook
|
||||
{
|
||||
_init(pages, isSubpage)
|
||||
{
|
||||
super._init({
|
||||
show_border: false,
|
||||
vexpand: true,
|
||||
hexpand: true,
|
||||
});
|
||||
|
||||
if(isSubpage) {
|
||||
this.set_tab_pos(Gtk.PositionType.LEFT);
|
||||
this.add_css_class('prefssubpage');
|
||||
}
|
||||
|
||||
this.addArrayPages(pages);
|
||||
}
|
||||
|
||||
addArrayPages(array)
|
||||
{
|
||||
for(let obj of array)
|
||||
this.addObjectPages(obj);
|
||||
}
|
||||
|
||||
addObjectPages(item)
|
||||
{
|
||||
const widget = (item.pages)
|
||||
? new Notebook(item.pages, true)
|
||||
: new item.widget();
|
||||
|
||||
this.addToNotebook(widget, item.title);
|
||||
}
|
||||
|
||||
addToNotebook(widget, title)
|
||||
{
|
||||
const label = new Gtk.Label({
|
||||
label: title,
|
||||
});
|
||||
this.append_page(widget, label);
|
||||
}
|
||||
|
||||
_onClose()
|
||||
{
|
||||
const totalPages = this.get_n_pages();
|
||||
let index = 0;
|
||||
|
||||
while(index < totalPages) {
|
||||
const page = this.get_nth_page(index);
|
||||
page._onClose();
|
||||
index++;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var Grid = GObject.registerClass(
|
||||
class ClapperPrefsGrid extends Gtk.Grid
|
||||
{
|
||||
_init()
|
||||
{
|
||||
super._init({
|
||||
row_spacing: 6,
|
||||
column_spacing: 20,
|
||||
});
|
||||
|
||||
this.flag = Gio.SettingsBindFlags.DEFAULT;
|
||||
this.gridIndex = 0;
|
||||
this.widgetDefaults = {
|
||||
width_request: 160,
|
||||
halign: Gtk.Align.END,
|
||||
valign: Gtk.Align.CENTER,
|
||||
};
|
||||
}
|
||||
|
||||
addToGrid(leftWidget, rightWidget)
|
||||
{
|
||||
let spanWidth = 2;
|
||||
|
||||
if(rightWidget) {
|
||||
spanWidth = 1;
|
||||
rightWidget.bind_property('visible', leftWidget, 'visible',
|
||||
GObject.BindingFlags.SYNC_CREATE
|
||||
);
|
||||
this.attach(rightWidget, 1, this.gridIndex, 1, 1);
|
||||
}
|
||||
|
||||
this.attach(leftWidget, 0, this.gridIndex, spanWidth, 1);
|
||||
this.gridIndex++;
|
||||
|
||||
return rightWidget || leftWidget;
|
||||
}
|
||||
|
||||
addTitle(text)
|
||||
{
|
||||
const label = this.getLabel(text, true);
|
||||
|
||||
return this.addToGrid(label);
|
||||
}
|
||||
|
||||
addComboBoxText(text, entries, setting)
|
||||
{
|
||||
const label = this.getLabel(text + ':');
|
||||
const widget = this.getComboBoxText(entries, setting);
|
||||
|
||||
return this.addToGrid(label, widget);
|
||||
}
|
||||
|
||||
addSpinButton(text, min, max, setting, precision)
|
||||
{
|
||||
const label = this.getLabel(text + ':');
|
||||
const widget = this.getSpinButton(min, max, setting, precision);
|
||||
|
||||
return this.addToGrid(label, widget);
|
||||
}
|
||||
|
||||
addCheckButton(text, setting)
|
||||
{
|
||||
const widget = this.getCheckButton(text, setting);
|
||||
|
||||
return this.addToGrid(widget);
|
||||
}
|
||||
|
||||
addPlayFlagCheckButton(text, flag)
|
||||
{
|
||||
const checkButton = this.addCheckButton(text);
|
||||
const playFlags = settings.get_int('play-flags');
|
||||
|
||||
checkButton.active = ((playFlags & flag) === flag);
|
||||
checkButton.connect('toggled', this._onPlayFlagToggled.bind(this, flag));
|
||||
|
||||
return checkButton;
|
||||
}
|
||||
|
||||
addFontButton(text, setting)
|
||||
{
|
||||
const label = this.getLabel(text + ':');
|
||||
const widget = this.getFontButton(setting);
|
||||
|
||||
return this.addToGrid(label, widget);
|
||||
}
|
||||
|
||||
getLabel(text, isTitle)
|
||||
{
|
||||
const marginTop = (isTitle && this.gridIndex > 0) ? 16 : 0;
|
||||
const marginBottom = (isTitle) ? 2 : 0;
|
||||
|
||||
let marginLR = 0;
|
||||
|
||||
if(isTitle)
|
||||
text = '<span font="12"><b>' + text + '</b></span>';
|
||||
else
|
||||
marginLR = 12;
|
||||
|
||||
return new Gtk.Label({
|
||||
label: text,
|
||||
use_markup: true,
|
||||
hexpand: true,
|
||||
halign: Gtk.Align.START,
|
||||
margin_top: marginTop,
|
||||
margin_bottom: marginBottom,
|
||||
margin_start: marginLR,
|
||||
margin_end: marginLR,
|
||||
});
|
||||
}
|
||||
|
||||
getComboBoxText(entries, setting)
|
||||
{
|
||||
const comboBox = new Gtk.ComboBoxText(this.widgetDefaults);
|
||||
|
||||
for(let entry of entries)
|
||||
comboBox.append(entry[0], entry[1]);
|
||||
|
||||
settings.bind(setting, comboBox, 'active-id', this.flag);
|
||||
|
||||
return comboBox;
|
||||
}
|
||||
|
||||
getSpinButton(min, max, setting, precision)
|
||||
{
|
||||
precision = precision || 1;
|
||||
|
||||
const spinButton = new Gtk.SpinButton(this.widgetDefaults);
|
||||
spinButton.set_range(min, max);
|
||||
spinButton.set_digits(precision % 1 === 0 ? 0 : 3);
|
||||
spinButton.set_increments(precision, 1);
|
||||
settings.bind(setting, spinButton, 'value', this.flag);
|
||||
|
||||
return spinButton;
|
||||
}
|
||||
|
||||
getCheckButton(text, setting)
|
||||
{
|
||||
const checkButton = new Gtk.CheckButton({
|
||||
label: text || null,
|
||||
});
|
||||
|
||||
if(setting)
|
||||
settings.bind(setting, checkButton, 'active', this.flag);
|
||||
|
||||
return checkButton;
|
||||
}
|
||||
|
||||
getFontButton(setting)
|
||||
{
|
||||
const fontButton = new Gtk.FontButton({
|
||||
use_font: true,
|
||||
use_size: true,
|
||||
});
|
||||
settings.bind(setting, fontButton, 'font', this.flag);
|
||||
|
||||
return fontButton;
|
||||
}
|
||||
|
||||
_onPlayFlagToggled(flag, button)
|
||||
{
|
||||
let playFlags = settings.get_int('play-flags');
|
||||
|
||||
if(button.active)
|
||||
playFlags |= flag;
|
||||
else
|
||||
playFlags &= ~flag;
|
||||
|
||||
settings.set_int('play-flags', playFlags);
|
||||
}
|
||||
|
||||
_onClose(name)
|
||||
{
|
||||
if(name)
|
||||
debug(`cleanup of prefs ${name} page`);
|
||||
}
|
||||
});
|
@@ -492,7 +492,7 @@ class ClapperWidget extends Gtk.Grid
|
||||
if(
|
||||
!this.isSeekable
|
||||
|| this.controls.isPositionDragging
|
||||
|| !player.seek_done
|
||||
|| !player.seekDone
|
||||
)
|
||||
return;
|
||||
|
||||
|
@@ -317,7 +317,7 @@ var YouTubeClient = GObject.registerClass({
|
||||
width: monitor.geometry.width * monitor.scale_factor,
|
||||
height: monitor.geometry.height * monitor.scale_factor,
|
||||
codec: 'h264',
|
||||
type: settings.get_string('yt-quality-type'),
|
||||
type: YTItags.QualityType[settings.get_int('yt-quality-type')],
|
||||
adaptive: settings.get_boolean('yt-adaptive-enabled'),
|
||||
};
|
||||
|
||||
|
@@ -1,3 +1,8 @@
|
||||
var QualityType = {
|
||||
0: 'normal',
|
||||
1: 'hfr',
|
||||
};
|
||||
|
||||
const Itags = {
|
||||
video: {
|
||||
h264: {
|
||||
|
30
ui/preferences-plugin-ranking-subpage.ui
Normal file
30
ui/preferences-plugin-ranking-subpage.ui
Normal file
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="ClapperPrefsPluginRankingSubpage" parent="GtkBox">
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="vexpand">True</property>
|
||||
<property name="hexpand">True</property>
|
||||
<child>
|
||||
<object class="AdwPreferencesPage">
|
||||
<child>
|
||||
<object class="AdwPreferencesGroup" id="decoders_group">
|
||||
<property name="title" translatable="yes">Decoders</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton">
|
||||
<property name="label" translatable="yes">Return to the preferences</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="margin_top">12</property>
|
||||
<property name="margin_bottom">12</property>
|
||||
<signal name="clicked" handler="_onReturnClicked"/>
|
||||
<style>
|
||||
<class name="suggested-action"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
</template>
|
||||
</interface>
|
292
ui/preferences-window.ui
Normal file
292
ui/preferences-window.ui
Normal file
@@ -0,0 +1,292 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="ClapperPrefsWindow" parent="AdwPreferencesWindow">
|
||||
<property name="title" translatable="yes">Preferences</property>
|
||||
<property name="resizable">True</property>
|
||||
<property name="search-enabled">True</property>
|
||||
<property name="destroy-with-parent">True</property>
|
||||
<property name="can-swipe-back">True</property>
|
||||
<property name="modal">True</property>
|
||||
<child>
|
||||
<object class="AdwPreferencesPage">
|
||||
<property name="title" translatable="yes">General</property>
|
||||
<property name="icon-name">user-home-symbolic</property>
|
||||
<child>
|
||||
<object class="AdwPreferencesGroup">
|
||||
<property name="title" translatable="yes">Behavior</property>
|
||||
<child>
|
||||
<object class="ClapperPrefsSwitch">
|
||||
<property name="title" translatable="yes">Auto fullscreen</property>
|
||||
<property name="subtitle" translatable="yes">Enter fullscreen when playlist is replaced except floating mode</property>
|
||||
<property name="schema-name">fullscreen-auto</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="ClapperPrefsSwitch">
|
||||
<property name="title" translatable="yes">Ask to resume recent media</property>
|
||||
<property name="schema-name">resume-enabled</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="ClapperPrefsSwitch">
|
||||
<property name="title" translatable="yes">Float on all workspaces</property>
|
||||
<property name="subtitle" translatable="yes">This option only works on GNOME</property>
|
||||
<property name="schema-name">floating-stick</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="ClapperPrefsCombo">
|
||||
<property name="title" translatable="yes">After playback</property>
|
||||
<property name="schema-name">after-playback</property>
|
||||
<property name="model">
|
||||
<object class="GtkStringList">
|
||||
<items>
|
||||
<item translatable="yes">Do nothing</item>
|
||||
<item translatable="yes">Freeze last frame</item>
|
||||
<item translatable="yes">Close the app</item>
|
||||
</items>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="AdwPreferencesGroup">
|
||||
<property name="title" translatable="yes">Volume</property>
|
||||
<child>
|
||||
<object class="ClapperPrefsExpander">
|
||||
<property name="title" translatable="yes">Custom initial value</property>
|
||||
<property name="subtitle" translatable="yes">Set custom volume at startup instead of restoring it</property>
|
||||
<property name="schema-name">volume-custom</property>
|
||||
<child>
|
||||
<object class="ClapperPrefsSpin">
|
||||
<property name="title" translatable="yes">Volume percentage</property>
|
||||
<property name="schema-name">volume-value</property>
|
||||
<property name="spin-adjustment">volume_adjustment</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="AdwPreferencesGroup">
|
||||
<property name="title" translatable="yes">Seeking</property>
|
||||
<child>
|
||||
<object class="ClapperPrefsCombo">
|
||||
<property name="title" translatable="yes">Mode</property>
|
||||
<property name="schema-name">seeking-mode</property>
|
||||
<property name="model">
|
||||
<object class="GtkStringList">
|
||||
<items>
|
||||
<item translatable="yes">Normal</item>
|
||||
<item translatable="yes">Accurate</item>
|
||||
<item translatable="yes">Fast</item>
|
||||
</items>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="ClapperPrefsCombo">
|
||||
<property name="title" translatable="yes">Unit</property>
|
||||
<property name="schema-name">seeking-unit</property>
|
||||
<property name="model">
|
||||
<object class="GtkStringList">
|
||||
<items>
|
||||
<item translatable="yes">Second</item>
|
||||
<item translatable="yes">Minute</item>
|
||||
<item translatable="yes">Percentage</item>
|
||||
</items>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="ClapperPrefsSpin">
|
||||
<property name="title" translatable="yes">Value</property>
|
||||
<property name="schema-name">seeking-value</property>
|
||||
<property name="spin-adjustment">seeking_adjustment</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="AdwPreferencesPage">
|
||||
<property name="title" translatable="yes">Playback</property>
|
||||
<property name="icon-name">camera-video-symbolic</property>
|
||||
<child>
|
||||
<object class="AdwPreferencesGroup">
|
||||
<property name="title" translatable="yes">Audio</property>
|
||||
<child>
|
||||
<object class="ClapperPrefsSpin">
|
||||
<property name="title" translatable="yes">Offset in milliseconds</property>
|
||||
<property name="schema-name">audio-offset</property>
|
||||
<property name="spin-adjustment">audio_offset_adjustment</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="ClapperPrefsPlayFlagSwitch">
|
||||
<property name="title" translatable="yes">Only native audio formats</property>
|
||||
<property name="play-flag">32</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="AdwPreferencesGroup">
|
||||
<property name="title" translatable="yes">Subtitles</property>
|
||||
<child>
|
||||
<object class="ClapperPrefsFont">
|
||||
<property name="title" translatable="yes">Default font</property>
|
||||
<property name="schema-name">subtitle-font</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="AdwPreferencesPage">
|
||||
<property name="title" translatable="yes">Network</property>
|
||||
<property name="icon-name">preferences-system-network-symbolic</property>
|
||||
<child>
|
||||
<object class="AdwPreferencesGroup">
|
||||
<property name="title" translatable="yes">Client</property>
|
||||
<child>
|
||||
<object class="ClapperPrefsPlayFlagSwitch">
|
||||
<property name="title" translatable="yes">Progressive download buffering</property>
|
||||
<property name="play-flag">128</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="AdwPreferencesGroup">
|
||||
<property name="title" translatable="yes">Server</property>
|
||||
<child>
|
||||
<object class="ClapperPrefsExpander">
|
||||
<property name="title" translatable="yes">Control player remotely</property>
|
||||
<property name="schema-name">webserver-enabled</property>
|
||||
<child>
|
||||
<object class="ClapperPrefsSpin">
|
||||
<property name="title" translatable="yes">Listening port</property>
|
||||
<property name="schema-name">webserver-port</property>
|
||||
<property name="spin-adjustment">web_server_adjustment</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="ClapperPrefsSwitch">
|
||||
<property name="title" translatable="yes">Run web application in background</property>
|
||||
<property name="subtitle" translatable="yes">Requires GTK compiled with Broadway backend</property>
|
||||
<property name="schema-name">webapp-enabled</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="ClapperPrefsSpin">
|
||||
<property name="title" translatable="yes">Web application port</property>
|
||||
<property name="schema-name">webapp-port</property>
|
||||
<property name="spin-adjustment">web_app_adjustment</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="AdwPreferencesGroup">
|
||||
<property name="title" translatable="yes">YouTube</property>
|
||||
<child>
|
||||
<object class="ClapperPrefsSwitch">
|
||||
<property name="title" translatable="yes">Prefer adaptive streaming</property>
|
||||
<property name="schema-name">yt-adaptive-enabled</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="ClapperPrefsCombo">
|
||||
<property name="title" translatable="yes">Max quality</property>
|
||||
<property name="schema-name">yt-quality-type</property>
|
||||
<property name="model">
|
||||
<object class="GtkStringList">
|
||||
<items>
|
||||
<item translatable="yes">Normal</item>
|
||||
<item translatable="no">HFR</item>
|
||||
</items>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="AdwPreferencesPage">
|
||||
<property name="title" translatable="yes">Tweaks</property>
|
||||
<property name="icon-name">applications-engineering-symbolic</property>
|
||||
<child>
|
||||
<object class="AdwPreferencesGroup">
|
||||
<property name="title" translatable="yes">Appearance</property>
|
||||
<child>
|
||||
<object class="ClapperPrefsSwitch">
|
||||
<property name="title" translatable="yes">Dark theme</property>
|
||||
<property name="schema-name">dark-theme</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="ClapperPrefsSwitch">
|
||||
<property name="title" translatable="yes">Render window shadows</property>
|
||||
<property name="subtitle" translatable="yes">Disable to increase performance when windowed</property>
|
||||
<property name="schema-name">render-shadows</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="AdwPreferencesGroup">
|
||||
<property name="title" translatable="yes">GStreamer</property>
|
||||
<child>
|
||||
<object class="ClapperPrefsPluginRankingSubpageRow">
|
||||
<property name="title" translatable="yes">Plugin ranking</property>
|
||||
<property name="subtitle" translatable="yes">Alter default ranks of GStreamer plugins</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</template>
|
||||
<object class="GtkAdjustment" id="seeking_adjustment">
|
||||
<property name="lower">0</property>
|
||||
<property name="upper">99</property>
|
||||
<property name="step-increment">1</property>
|
||||
<property name="page-increment">1</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="volume_adjustment">
|
||||
<property name="lower">0</property>
|
||||
<property name="upper">150</property>
|
||||
<property name="step-increment">1</property>
|
||||
<property name="page-increment">1</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="audio_offset_adjustment">
|
||||
<property name="lower">-1000</property>
|
||||
<property name="upper">1000</property>
|
||||
<property name="step-increment">25</property>
|
||||
<property name="page-increment">1</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="web_server_adjustment">
|
||||
<property name="lower">1024</property>
|
||||
<property name="upper">65535</property>
|
||||
<property name="step-increment">1</property>
|
||||
<property name="page-increment">1</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="web_app_adjustment">
|
||||
<property name="lower">1024</property>
|
||||
<property name="upper">65535</property>
|
||||
<property name="step-increment">1</property>
|
||||
<property name="page-increment">1</property>
|
||||
</object>
|
||||
</interface>
|
Reference in New Issue
Block a user