add feature page
Allow for additional settings that are not just software packages. Fixes #13
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><path d="m 6.5 1 c -0.832031 0 -1.5 0.667969 -1.5 1.5 v 1.5 h -3 c -0.554688 0 -1 0.445312 -1 1 v 3 h 1.5 c 0.832031 0 1.5 0.667969 1.5 1.5 s -0.667969 1.5 -1.5 1.5 h -1.5 v 3 c 0 0.554688 0.445312 1 1 1 h 3 v -1.5 c 0 -0.832031 0.667969 -1.5 1.5 -1.5 s 1.5 0.667969 1.5 1.5 v 1.5 h 3 c 0.554688 0 1 -0.445312 1 -1 v -3 h 1.5 c 0.832031 0 1.5 -0.667969 1.5 -1.5 s -0.667969 -1.5 -1.5 -1.5 h -1.5 v -3 c 0 -0.554688 -0.445312 -1 -1 -1 h -3 v -1.5 c 0 -0.832031 -0.667969 -1.5 -1.5 -1.5 z m 0 0" fill="#888888"/></svg>
|
||||
|
After Width: | Height: | Size: 643 B |
@@ -9,6 +9,7 @@ blueprints = custom_target('blueprints',
|
||||
'ui/pages/done.blp',
|
||||
'ui/pages/encrypt.blp',
|
||||
'ui/pages/failed.blp',
|
||||
'ui/pages/feature.blp',
|
||||
'ui/pages/format.blp',
|
||||
'ui/pages/install.blp',
|
||||
'ui/pages/internet.blp',
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
<file preprocess="xml-stripblanks">icon/scalable/actions/language-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icon/scalable/actions/map-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icon/scalable/actions/no-disk-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icon/scalable/actions/puzzle-piece-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icon/scalable/actions/question-round-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icon/scalable/actions/success-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icon/scalable/actions/user-symbolic.svg</file>
|
||||
@@ -27,6 +28,7 @@
|
||||
<file preprocess="xml-stripblanks">ui/pages/done.ui</file>
|
||||
<file preprocess="xml-stripblanks">ui/pages/encrypt.ui</file>
|
||||
<file preprocess="xml-stripblanks">ui/pages/failed.ui</file>
|
||||
<file preprocess="xml-stripblanks">ui/pages/feature.ui</file>
|
||||
<file preprocess="xml-stripblanks">ui/pages/format.ui</file>
|
||||
<file preprocess="xml-stripblanks">ui/pages/install.ui</file>
|
||||
<file preprocess="xml-stripblanks">ui/pages/internet.ui</file>
|
||||
|
||||
35
data/resources/ui/pages/feature.blp
Normal file
35
data/resources/ui/pages/feature.blp
Normal file
@@ -0,0 +1,35 @@
|
||||
using Gtk 4.0;
|
||||
|
||||
template FeaturePage : Box {
|
||||
orientation: vertical;
|
||||
spacing: 12;
|
||||
|
||||
Label {
|
||||
/* Translators: Page title */
|
||||
label: _("Additional Features");
|
||||
justify: center;
|
||||
wrap: true;
|
||||
styles ["heading"]
|
||||
}
|
||||
|
||||
ScrolledWindow {
|
||||
propagate-natural-height: true;
|
||||
styles ["embedded", "separate-bottom"]
|
||||
child: ListBox list {
|
||||
hexpand: true;
|
||||
selection-mode: none;
|
||||
row-activated => row_activated();
|
||||
styles ["boxed-list"]
|
||||
};
|
||||
}
|
||||
|
||||
Button {
|
||||
/* Translators: On button. */
|
||||
label: _("_Continue");
|
||||
focusable: true;
|
||||
halign: center;
|
||||
use-underline: true;
|
||||
clicked => continue();
|
||||
styles ["suggested-action", "pill", "bottom-button"]
|
||||
}
|
||||
}
|
||||
@@ -167,6 +167,46 @@ template SummaryPage : Box {
|
||||
icon-name: "emblem-system-symbolic";
|
||||
}
|
||||
}
|
||||
|
||||
Adw.ActionRow feature_row {
|
||||
name: "feature";
|
||||
activatable: true;
|
||||
/* Translators: Description of selected additional software. */
|
||||
title: _("Additional Features");
|
||||
|
||||
Stack feature_stack {
|
||||
vhomogeneous: false;
|
||||
transition-type: crossfade;
|
||||
styles ["row-content"]
|
||||
|
||||
StackPage {
|
||||
name: "used";
|
||||
child: ListBox feature_list {
|
||||
halign: end;
|
||||
valign: center;
|
||||
selection-mode: none;
|
||||
styles ["nested-list"]
|
||||
};
|
||||
}
|
||||
|
||||
StackPage {
|
||||
name: "none";
|
||||
child: Label {
|
||||
/* Translators: Shown when list of selected feature is empty. */
|
||||
label: _("None");
|
||||
hexpand: true;
|
||||
valign: center;
|
||||
wrap: true;
|
||||
xalign: 1;
|
||||
styles ["dim-label"]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
icon-name: "emblem-system-symbolic";
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -100,6 +100,31 @@ additional_software:
|
||||
name_jp : '入力プログラム'
|
||||
icon_path : '/etc/os-installer/icons/jp-symbol.svg'
|
||||
|
||||
# List of features that can additionally be selected. Very similar
|
||||
# to `additional_software`, but meant for more generic features. Can
|
||||
# be used instead of or in combination with `additional_software`.
|
||||
#
|
||||
# feature string Forwarded to the installation script as is.
|
||||
# suggested bool Optional. Whether installation defaults to yes.
|
||||
# name_LC string Name presented to user. Translatable.
|
||||
# If no (English) name is available, it will only
|
||||
# be shown for translated languages.
|
||||
# description_LC string Optional. Description presented to user. Translatable.
|
||||
# icon_path string Optional. Absolute path to icon to be displayed.
|
||||
#
|
||||
# Default: [], suggested: False, description: '', icon_path: no icon
|
||||
additional_features:
|
||||
- feature : 'snapshots'
|
||||
suggested : yes
|
||||
name : 'Snapshots'
|
||||
name_et : 'Vahepildid'
|
||||
description : 'Snapshots allow restoring a previous state of your system'
|
||||
description_et : 'Vahepildid võimaldavad taastada teie süsteemi eelmise seisundi'
|
||||
icon_path : '/etc/os-installer/icons/snapshot.svg'
|
||||
- feature : 'dummy'
|
||||
name : 'Dummy'
|
||||
description : 'This does not do anything'
|
||||
|
||||
# Upon failure an option to search for help on the internet is given.
|
||||
# The url this leads to can be defined here. The squiggly brackets are
|
||||
# replaced with the os-installer version.
|
||||
|
||||
50
example_config/icons/snapshot.svg
Normal file
50
example_config/icons/snapshot.svg
Normal file
@@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
height="32"
|
||||
viewBox="0 0 32 32"
|
||||
width="32"
|
||||
version="1.1"
|
||||
id="svg4"
|
||||
sodipodi:docname="snapshot.svg"
|
||||
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14, custom)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs8" />
|
||||
<sodipodi:namedview
|
||||
id="namedview6"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showgrid="false"
|
||||
inkscape:zoom="13.40625"
|
||||
inkscape:cx="28.606061"
|
||||
inkscape:cy="32"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1011"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g6571" />
|
||||
<g
|
||||
id="g6571"
|
||||
transform="scale(4)">
|
||||
<circle
|
||||
style="fill:#ffffff;fill-opacity:1;stroke-width:0.5;stroke-dasharray:none"
|
||||
id="path8389"
|
||||
cx="4"
|
||||
cy="4"
|
||||
r="4" />
|
||||
<path
|
||||
d="M 4,2 C 3.475586,2 2.972656,2.206055 2.598633,2.573242 L 3.27832,3.75 4.276367,2.021484 C 4.18457,2.007812 4.092773,2.000976 4,2 Z M 4.53418,2.0752 3.855469,3.25 h 1.99707 C 5.61914,2.674805 5.132812,2.240234 4.53418,2.075195 Z M 2.424805,2.771489 C 2.15039,3.12207 2.000977,3.554687 2,4 2,4.168945 2.02246,4.336914 2.06543,4.5 H 3.422852 Z M 4.577148,3.5 5.575195,5.228515 C 5.849609,4.87793 5.999023,4.445312 6,4 6,3.831055 5.97754,3.663086 5.93457,3.5 Z M 4.72168,4.25 3.723633,5.978515 C 3.815433,5.992187 3.907226,5.999023 4,6 4.524414,6 5.027344,5.793946 5.401367,5.425782 Z M 2.147461,4.75 C 2.380859,5.325195 2.867187,5.759765 3.46582,5.924805 L 4.144531,4.75 Z m 0,0"
|
||||
fill="#222222"
|
||||
id="path8380"
|
||||
style="stroke-width:0.25" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
@@ -10,6 +10,7 @@
|
||||
# OSI_FORMATS : Locale of formats to be used
|
||||
# OSI_TIMEZONE : Timezone to be used
|
||||
# OSI_ADDITIONAL_SOFTWARE: Space-separated list of additional packages to install
|
||||
# OSI_ADDITIONAL_FEATURES: Space-separated list of additional features chosen
|
||||
|
||||
# sanity check that all variables were set
|
||||
if [ -z ${OSI_LOCALE+x} ] || \
|
||||
@@ -23,7 +24,8 @@ if [ -z ${OSI_LOCALE+x} ] || \
|
||||
[ -z ${OSI_USER_PASSWORD+x} ] || \
|
||||
[ -z ${OSI_FORMATS+x} ] || \
|
||||
[ -z ${OSI_TIMEZONE+x} ] || \
|
||||
[ -z ${OSI_ADDITIONAL_SOFTWARE+x} ]
|
||||
[ -z ${OSI_ADDITIONAL_SOFTWARE+x} ] || \
|
||||
[ -z ${OSI_ADDITIONAL_FEATURES+x} ]
|
||||
then
|
||||
echo "Installer script called without all environment variables set!"
|
||||
exit 1
|
||||
@@ -44,6 +46,7 @@ echo 'OSI_USER_PASSWORD ' $OSI_USER_PASSWORD
|
||||
echo 'OSI_FORMATS ' $OSI_FORMATS
|
||||
echo 'OSI_TIMEZONE ' $OSI_TIMEZONE
|
||||
echo 'OSI_ADDITIONAL_SOFTWARE ' $OSI_ADDITIONAL_SOFTWARE
|
||||
echo 'OSI_ADDITIONAL_FEATURES ' $OSI_ADDITIONAL_FEATURES
|
||||
echo ''
|
||||
|
||||
# Pretending to do something
|
||||
|
||||
@@ -54,6 +54,8 @@ def _load_default_config():
|
||||
'skip_locale': False,
|
||||
# software
|
||||
'additional_software': [],
|
||||
# feature
|
||||
'additional_features': [],
|
||||
# fail
|
||||
'failure_help_url': 'https://duckduckgo.com/?q="os-installer {}"+"failed installation"',
|
||||
# commands
|
||||
@@ -77,6 +79,8 @@ def _load_optional_defaults(config):
|
||||
config['timezone'] = 'UTC'
|
||||
config['chosen_software_packages'] = ''
|
||||
config['chosen_software'] = []
|
||||
config['chosen_features'] = ''
|
||||
config['chosen_feature_names'] = []
|
||||
|
||||
|
||||
def _set_testing_defaults(config):
|
||||
@@ -105,6 +109,7 @@ def _valid(config):
|
||||
_match(config, 'minimum_disk_size', int) and
|
||||
_match(config, 'offer_disk_encryption', bool) and
|
||||
_match(config, 'additional_software', list) and
|
||||
_match(config, 'additional_features', list) and
|
||||
_match(config, 'distribution_name', str) and
|
||||
_match(config, 'fixed_language', bool, str))
|
||||
|
||||
@@ -155,5 +160,6 @@ def create_envs(config, with_install_envs, with_configure_envs):
|
||||
f'OSI_FORMATS={config["formats_locale"]}',
|
||||
f'OSI_TIMEZONE={config["timezone"]}',
|
||||
f'OSI_ADDITIONAL_SOFTWARE={config["chosen_software_packages"]}',
|
||||
f'OSI_ADDITIONAL_FEATURES={config["chosen_features"]}',
|
||||
]
|
||||
return envs + [None]
|
||||
|
||||
@@ -23,6 +23,7 @@ os_installer_sources = [
|
||||
'global_state.py',
|
||||
'main.py',
|
||||
'provider/disk_provider.py',
|
||||
'provider/feature_provider.py',
|
||||
'provider/format_provider.py',
|
||||
'provider/internet_provider.py',
|
||||
'provider/keyboard_layout_provider.py',
|
||||
@@ -36,6 +37,7 @@ os_installer_sources = [
|
||||
'ui/pages/done.py',
|
||||
'ui/pages/encrypt.py',
|
||||
'ui/pages/failed.py',
|
||||
'ui/pages/feature.py',
|
||||
'ui/pages/format.py',
|
||||
'ui/pages/install.py',
|
||||
'ui/pages/internet.py',
|
||||
|
||||
51
src/provider/feature_provider.py
Normal file
51
src/provider/feature_provider.py
Normal file
@@ -0,0 +1,51 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from gi.repository import GObject
|
||||
from .global_state import global_state
|
||||
|
||||
|
||||
class Feature(GObject.GObject):
|
||||
__gtype_name__ = __qualname__
|
||||
|
||||
def __init__(self, feature, suggested, name, description, icon_path):
|
||||
super().__init__()
|
||||
|
||||
self.feature = feature
|
||||
self.suggested = suggested
|
||||
self.name = name
|
||||
self.description = description
|
||||
self.icon_path = icon_path
|
||||
|
||||
|
||||
### public methods ###
|
||||
def get_feature_suggestions():
|
||||
if not (features := global_state.get_config('additional_features')):
|
||||
return []
|
||||
language_code = global_state.get_config('language_code')
|
||||
|
||||
suggestions = []
|
||||
for feature in features:
|
||||
if not 'feature' in feature:
|
||||
print(f'feature {feature} not correctly configured!')
|
||||
continue
|
||||
|
||||
if (not ((name_key := f'name_{language_code}') in feature or
|
||||
(name_key := 'name') in feature)):
|
||||
# no error if feature is only suggested for specific translations
|
||||
if not any(key.startswith('name') for key in feature.keys()):
|
||||
print(f'feature {feature} not correctly configured!')
|
||||
continue
|
||||
|
||||
suggested = feature['suggested'] if 'suggested' in feature else False
|
||||
name = feature[name_key]
|
||||
if ((description_key := f'description_{language_code}') in feature or
|
||||
(description_key := 'description') in feature):
|
||||
description = feature[description_key]
|
||||
else:
|
||||
description = ''
|
||||
|
||||
icon_path = feature['icon_path'] if 'icon_path' in feature else ''
|
||||
suggestions.append(
|
||||
Feature(feature['feature'], suggested, name, description, icon_path))
|
||||
|
||||
return suggestions
|
||||
46
src/ui/pages/feature.py
Normal file
46
src/ui/pages/feature.py
Normal file
@@ -0,0 +1,46 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from gi.repository import Gio, Gtk
|
||||
|
||||
from .global_state import global_state
|
||||
from .page import Page
|
||||
from .feature_provider import get_feature_suggestions
|
||||
from .widgets import reset_model, SelectionRow
|
||||
|
||||
|
||||
@Gtk.Template(resource_path='/com/github/p3732/os-installer/ui/pages/feature.ui')
|
||||
class FeaturePage(Gtk.Box, Page):
|
||||
__gtype_name__ = __qualname__
|
||||
image = 'puzzle-piece-symbolic'
|
||||
|
||||
list = Gtk.Template.Child()
|
||||
list_model = Gio.ListStore()
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
Gtk.Box.__init__(self, **kwargs)
|
||||
self.list.bind_model(
|
||||
self.list_model,
|
||||
lambda feat: SelectionRow(feat.name, feat.description, feat.icon_path,
|
||||
feat.suggested, feat, 'puzzle-piece-symbolic'))
|
||||
|
||||
### callbacks ###
|
||||
|
||||
@Gtk.Template.Callback('continue')
|
||||
def _continue(self, button):
|
||||
global_state.advance(self)
|
||||
|
||||
@Gtk.Template.Callback('row_activated')
|
||||
def _row_activated(self, list_box, row):
|
||||
row.flip_switch()
|
||||
|
||||
### public methods ###
|
||||
|
||||
def load_once(self):
|
||||
suggestions = get_feature_suggestions()
|
||||
reset_model(self.list_model, suggestions)
|
||||
|
||||
def unload(self):
|
||||
choices = [row.info for row in self.list if row.is_activated()]
|
||||
features = ' '.join([choice.feature for choice in choices])
|
||||
global_state.set_config('chosen_feature_names', choices)
|
||||
global_state.set_config('chosen_features', features)
|
||||
@@ -5,7 +5,6 @@ from gi.repository import Gio, Gtk
|
||||
from .global_state import global_state
|
||||
from .installation_scripting import installation_scripting, Step
|
||||
from .page import Page
|
||||
#from .software_provider import get_software_suggestions
|
||||
from .widgets import reset_model, SoftwareSummaryRow
|
||||
|
||||
|
||||
@@ -21,6 +20,7 @@ class SummaryPage(Gtk.Box, Page):
|
||||
format_row = Gtk.Template.Child()
|
||||
timezone_row = Gtk.Template.Child()
|
||||
software_row = Gtk.Template.Child()
|
||||
feature_row = Gtk.Template.Child()
|
||||
|
||||
# row content
|
||||
language_label = Gtk.Template.Child()
|
||||
@@ -35,12 +35,20 @@ class SummaryPage(Gtk.Box, Page):
|
||||
software_list = Gtk.Template.Child()
|
||||
software_model = Gio.ListStore()
|
||||
|
||||
# feature list
|
||||
feature_stack = Gtk.Template.Child()
|
||||
feature_list = Gtk.Template.Child()
|
||||
feature_model = Gio.ListStore()
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
Gtk.Box.__init__(self, **kwargs)
|
||||
self.software_list.bind_model(
|
||||
self.software_model, lambda pkg: SoftwareSummaryRow(pkg.name, pkg.icon_path))
|
||||
self.feature_list.bind_model(
|
||||
self.feature_model, lambda pkg: SoftwareSummaryRow(pkg.name, pkg.icon_path))
|
||||
self.language_row.set_visible(global_state.get_config('fixed_language'))
|
||||
self.software_row.set_visible(global_state.get_config('additional_software'))
|
||||
self.feature_row.set_visible(global_state.get_config('additional_features'))
|
||||
self.user_row.set_visible(not global_state.get_config('skip_user'))
|
||||
self.format_row.set_visible(not global_state.get_config('skip_locale'))
|
||||
self.timezone_row.set_visible(not global_state.get_config('skip_locale'))
|
||||
@@ -75,4 +83,11 @@ class SummaryPage(Gtk.Box, Page):
|
||||
else:
|
||||
self.software_stack.set_visible_child_name('none')
|
||||
|
||||
features = global_state.get_config('chosen_feature_names')
|
||||
if len(features) > 0:
|
||||
self.feature_stack.set_visible_child_name('used')
|
||||
reset_model(self.feature_model, features)
|
||||
else:
|
||||
self.feature_stack.set_visible_child_name('none')
|
||||
|
||||
return "prevent_back_navigation"
|
||||
|
||||
@@ -13,6 +13,7 @@ from .disk import DiskPage
|
||||
from .done import DonePage
|
||||
from .encrypt import EncryptPage
|
||||
from .failed import FailedPage
|
||||
from .feature import FeaturePage
|
||||
from .format import FormatPage
|
||||
from .install import InstallPage
|
||||
from .internet import InternetPage
|
||||
@@ -107,6 +108,7 @@ class OsInstallerWindow(Adw.ApplicationWindow):
|
||||
('format', FormatPage, not global_state.get_config('skip_locale')),
|
||||
('timezone', TimezonePage, not global_state.get_config('skip_locale')),
|
||||
('software', SoftwarePage, global_state.get_config('additional_software')),
|
||||
('feature', FeaturePage, global_state.get_config('additional_features')),
|
||||
# summary
|
||||
('summary', SummaryPage, True),
|
||||
# installation
|
||||
|
||||
Reference in New Issue
Block a user