Skip to content

Commit

Permalink
envvars-dialog: Allow to paste var=value and fix validation
Browse files Browse the repository at this point in the history
* Rework the envvars dialog to allow copy/pasting complete variable assignments instead of having to input first the name and later the value.
* Fix the input validation which had some inverted logic and make it behave as expected.
* Make the panel leaner by removing unnecessary headers and periods at the end of labels.
  • Loading branch information
jntesteves committed Jan 7, 2025
1 parent 98cce18 commit fae1add
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 40 deletions.
10 changes: 4 additions & 6 deletions bottles/frontend/ui/dialog-env-vars.blp
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,15 @@ template $EnvVarsDialog: Adw.Window {

Adw.PreferencesPage {
Adw.PreferencesGroup {
description: _("Environment variables are dynamic-named value that can affect the way running processes will behave on your bottle.");
description: _("Environment variables are dynamic-named values that can affect the way running processes will behave in your bottle");

Adw.EntryRow entry_name {
title: _("Variable Name");
Adw.EntryRow entry_new_var {
title: _("New Variable");
show-apply-button: true;
}
}

Adw.PreferencesGroup group_vars {
title: _("Existing Variables");
}
Adw.PreferencesGroup group_vars {}
}
}
}
1 change: 0 additions & 1 deletion bottles/frontend/ui/env-var-entry.blp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ using Gtk 4.0;
using Adw 1;

template $EnvVarEntry: Adw.EntryRow {
title: _("Value");
show-apply-button: true;

Button btn_remove {
Expand Down
13 changes: 7 additions & 6 deletions bottles/frontend/utils/gtk.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,28 +15,29 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#

import re
from typing import Optional
from functools import wraps
from inspect import signature

from gi.repository import GLib, Gtk

from bottles.frontend.utils.sh import ShUtils


class GtkUtils:
@staticmethod
def validate_entry(entry, extend=None) -> bool:
text = entry.get_text()
var_assignment=entry.get_text()
var_name = ShUtils.split_assignment(var_assignment)[0]
if (
re.search("[@!#$%^&*()<>?/|}{~:.;,'\"]", text)
or len(text) == 0
or text.isspace()
"=" not in var_assignment
or not ShUtils.is_name(var_name)
):
entry.add_css_class("error")
return False

if extend is not None:
if extend(text):
if not extend(var_name):
entry.add_css_class("error")
return False

Expand Down
1 change: 1 addition & 0 deletions bottles/frontend/utils/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ bottles_sources = [
'gtk.py',
'common.py',
'filters.py',
'sh.py',
]

install_data(bottles_sources, install_dir: utilsdir)
30 changes: 30 additions & 0 deletions bottles/frontend/utils/sh.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# sh.py
#
# Copyright 2025 The Bottles Contributors
#
# 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, in version 3 of the License.
#
# 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/>.
#

import re

_is_name = re.compile(r'''[_a-zA-Z][_a-zA-Z0-9]*''')

class ShUtils:
@staticmethod
def is_name(text: str) -> bool:
return bool(_is_name.fullmatch(text))

@staticmethod
def split_assignment(text: str) -> tuple[str, str]:
name, _, value = text.partition("=")
return (name, value)
68 changes: 41 additions & 27 deletions bottles/frontend/windows/envvars.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from gi.repository import Gtk, GLib, Adw

from bottles.frontend.utils.gtk import GtkUtils
from bottles.frontend.utils.sh import ShUtils


@Gtk.Template(resource_path="/com/usebottles/bottles/env-var-entry.ui")
Expand All @@ -36,11 +37,10 @@ def __init__(self, parent, env, **kwargs):
self.manager = parent.window.manager
self.config = parent.config
self.env = env

self.set_title(self.env[0])
self.set_text(self.env[1])
self.set_text("=".join(self.env))

# connect signals
self.connect("changed", self.__validate)
self.connect("apply", self.__save)
self.btn_remove.connect("clicked", self.__remove)

Expand All @@ -49,34 +49,53 @@ def __save(self, *_args):
Change the env var value according to the
user input and update the bottle configuration
"""
if not self.__valid_name:
return

new_name, new_value = ShUtils.split_assignment(self.get_text())
self.manager.update_config(
config=self.config,
key=self.env[0],
value=self.get_text(),
key=new_name,
value=new_value,
scope="Environment_Variables",
)
if new_name != self.env[0]:
self.__remove_config()

self.env = (new_name, new_value)

def __remove(self, *_args):
"""
Remove the env var from the bottle configuration and
destroy the widget
"""
self.__remove_config()
self.parent.group_vars.remove(self)

def __remove_config(self, *_args):
"""
Remove the env var from the bottle configuration
"""
self.manager.update_config(
config=self.config,
key=self.env[0],
value=False,
remove=True,
scope="Environment_Variables",
)
self.parent.group_vars.remove(self)

def __validate(self, *_args):
self.__valid_name = GtkUtils.validate_entry(
self, lambda var_name: not var_name == "WINEDLLOVERRIDES"
)


@Gtk.Template(resource_path="/com/usebottles/bottles/dialog-env-vars.ui")
class EnvVarsDialog(Adw.Window):
__gtype_name__ = "EnvVarsDialog"

# region Widgets
entry_name = Gtk.Template.Child()
entry_new_var = Gtk.Template.Child()
group_vars = Gtk.Template.Child()
# endregion

Expand All @@ -92,40 +111,36 @@ def __init__(self, window, config, **kwargs):
self.__populate_vars_list()

# connect signals
self.entry_name.connect("changed", self.__validate)
self.entry_name.connect("apply", self.__save_var)
self.entry_new_var.connect("changed", self.__validate)
self.entry_new_var.connect("apply", self.__save_var)

def __validate(self, *_args):
self.__valid_name = GtkUtils.validate_entry(
self.entry_name, lambda envvar: envvar.startswith("WINEDLLOVERRIDES")
self.entry_new_var, lambda var_name: not var_name == "WINEDLLOVERRIDES"
)

if not self.entry_new_var.get_text():
self.entry_new_var.remove_css_class("error")

def __save_var(self, *_args):
"""
This function save the new env var to the
bottle configuration
"""
if not self.__valid_name:
self.entry_name.set_text("")
self.entry_name.remove_css_class("error")
self.__valid_name = True
return

env_name = self.entry_name.get_text()
env_value = "value"
split_value = env_name.split("=", 1)
if len(split_value) == 2:
env_name = split_value[0]
env_value = split_value[1]
new_name, new_value = ShUtils.split_assignment(self.entry_new_var.get_text())
self.manager.update_config(
config=self.config,
key=env_name,
value=env_value,
key=new_name,
value=new_value,
scope="Environment_Variables",
)
_entry = EnvVarEntry(parent=self, env=[env_name, env_value])
GLib.idle_add(self.group_vars.add, _entry)
self.entry_name.set_text("")
_entry = EnvVarEntry(parent=self, env=(new_name, new_value))
self.group_vars.set_description()
self.group_vars.add(_entry)
self.entry_new_var.set_text("")

def __populate_vars_list(self):
"""
Expand All @@ -134,10 +149,9 @@ def __populate_vars_list(self):
"""
envs = self.config.Environment_Variables.items()
if len(envs) == 0:
self.group_vars.set_description(_("No environment variables defined."))
self.group_vars.set_description(_("No environment variables defined"))
return

self.group_vars.set_description("")
for env in envs:
_entry = EnvVarEntry(parent=self, env=env)
GLib.idle_add(self.group_vars.add, _entry)
self.group_vars.add(_entry)

0 comments on commit fae1add

Please sign in to comment.