feat: macro prompts close #1216 (#1219)

* feat: macro prompts close #1216

now the user can have confirmation on macros using this feature close #835

* prompt: remove separator and remove the header and close button if windowed

* prompt: wakeup screen

* prompt: slightly larger close icon
This commit is contained in:
Alfredo Monclus 2024-01-02 08:04:02 -03:00 committed by GitHub
parent 25ab9c962d
commit 1794d8aa24
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 301 additions and 92 deletions

View File

@ -218,21 +218,30 @@ class KlippyGtk:
if not self.screen.windowed:
dialog.fullscreen()
max_buttons = 3 if self.screen.vertical_mode else 4
if len(buttons) > max_buttons:
buttons = buttons[:max_buttons]
button_hsize = max(len(buttons), 3)
for button in buttons:
if 'style' in button:
style = button['style']
else:
style = 'dialog-default'
dialog.add_button(button['name'], button['response'])
button = dialog.get_widget_for_response(button['response'])
button.set_size_request((self.width - 30) / 3, self.height / 5)
format_label(button, 3)
button.set_size_request((self.width - 58) / button_hsize, self.height / 5)
button.get_style_context().add_class(style)
format_label(button, 2)
dialog.connect("response", self.screen.reset_screensaver_timeout)
dialog.connect("response", callback, *args)
dialog.get_style_context().add_class("dialog")
content_area = dialog.get_content_area()
content_area.set_margin_start(15)
content_area.set_margin_end(15)
content_area.set_margin_top(15)
content_area.set_margin_bottom(15)
content_area.set_margin_start(5)
content_area.set_margin_end(5)
content_area.set_margin_top(5)
content_area.set_margin_bottom(0)
content.set_valign(Gtk.Align.CENTER)
content_area.add(content)

View File

@ -0,0 +1,106 @@
import logging
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
class Prompt:
def __init__(self, screen):
self.screen = screen
self.gtk = screen.gtk
self.window_title = 'KlipperScreen'
self.text = self.header = ""
self.buttons = []
self.id = 1
self.prompt = None
def decode(self, data):
logging.info(f'{data}')
if data.startswith('prompt_begin'):
# action:prompt_begin <headline>
self.header = data.replace('prompt_begin', '')
if self.header:
self.window_title = self.header
self.text = ""
self.buttons = []
return
elif data.startswith('prompt_text'):
# action:prompt_text <text>
self.text = data.replace('prompt_text ', '')
return
elif data.startswith('prompt_button ') or data.startswith('prompt_footer_button'):
# action:prompt_button <label>|<gcode?>|<color?>
# <label>: text of the button
# <gcode?>: optional G-Code (Default is the label text)
# <color?>: optional secondary, info, warning, error
data = data.replace('prompt_button ', '')
data = data.replace('prompt_footer_button ', '')
params = data.split('|')
if len(params) == 1:
params.append(self.text)
if len(params) > 3:
logging.error('Unexpected number of parameters on the button')
return
self.set_button(*params)
return
elif data == 'prompt_show':
self.show()
return
elif data == 'prompt_end':
self.end()
else:
# Not implemented:
# prompt_button_group_start
# prompt_button_group_end
logging.debug(f'Unknown option {data}')
def set_button(self, name, gcode, style='default'):
logging.info(f'{name} {self.id} {gcode} {style}')
self.buttons.append(
{"name": name, "response": self.id, 'gcode': gcode, 'style': f'dialog-{style}'}
)
self.id += 1
def show(self):
logging.info(f'Prompt {self.header} {self.text} {self.buttons}')
title = Gtk.Label(wrap=True, hexpand=True, vexpand=False, halign=Gtk.Align.CENTER, label=self.header)
close = self.gtk.Button("cancel", scale=self.gtk.bsidescale)
close.set_hexpand(False)
close.connect("clicked", self.end)
scroll = self.gtk.ScrolledWindow(steppers=False)
scroll.set_vexpand(True)
if self.screen.vertical_mode:
scroll.set_size_request(self.gtk.width - 30, self.gtk.height * .6)
else:
scroll.set_size_request(self.gtk.width - 30, self.gtk.height * .4)
scroll.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
scroll.add(Gtk.Label(label=self.text, wrap=True, vexpand=True))
content = Gtk.Grid()
if not self.screen.windowed:
content.attach(title, 0, 0, 1, 1)
content.attach(close, 1, 0, 1, 1)
content.attach(scroll, 0, 1, 2, 1)
self.prompt = self.gtk.Dialog(
self.window_title,
self.buttons,
content,
self.response,
)
def response(self, dialog, response_id):
for button in self.buttons:
if button['response'] == response_id:
self.screen._send_action(None, "printer.gcode.script", {'script': button['gcode']})
self.end()
def end(self, *args):
if self.prompt is not None:
self.gtk.remove_dialog(self.prompt)
self.prompt = None
self.screen.prompt = None

View File

@ -309,7 +309,7 @@ class BasePanel(ScreenPanel):
def show_update_dialog(self):
if self.update_dialog is not None:
return
button = [{"name": _("Finish"), "response": Gtk.ResponseType.OK}]
button = [{"name": _("Finish"), "response": Gtk.ResponseType.OK, "style": 'dialog-default'}]
self.labels['update_progress'] = Gtk.Label()
self.labels['update_progress'].set_halign(Gtk.Align.START)
self.labels['update_progress'].set_valign(Gtk.Align.START)

View File

@ -402,8 +402,8 @@ class Panel(ScreenPanel):
grid = self._gtk.HomogeneousGrid()
grid.attach(label, 0, 0, 1, 1)
buttons = [
{"name": _("Apply"), "response": Gtk.ResponseType.APPLY},
{"name": _("Cancel"), "response": Gtk.ResponseType.CANCEL}
{"name": _("Apply"), "response": Gtk.ResponseType.APPLY, "style": 'dialog-default'},
{"name": _("Cancel"), "response": Gtk.ResponseType.CANCEL, "style": 'dialog-error'}
]
self._gtk.Dialog(_("Save Z"), buttons, grid, self.save_confirm, device)
@ -438,8 +438,8 @@ class Panel(ScreenPanel):
def cancel(self, widget):
buttons = [
{"name": _("Cancel Print"), "response": Gtk.ResponseType.OK},
{"name": _("Go Back"), "response": Gtk.ResponseType.CANCEL}
{"name": _("Cancel Print"), "response": Gtk.ResponseType.OK, "style": 'dialog-error'},
{"name": _("Go Back"), "response": Gtk.ResponseType.CANCEL, "style": 'dialog-info'}
]
if len(self._printer.get_stat("exclude_object", "objects")) > 1:
buttons.insert(0, {"name": _("Exclude Object"), "response": Gtk.ResponseType.APPLY})

View File

@ -300,7 +300,7 @@ class Panel(ScreenPanel):
buttons = [
{"name": _("Print"), "response": Gtk.ResponseType.OK},
{"name": _("Cancel"), "response": Gtk.ResponseType.CANCEL}
{"name": _("Cancel"), "response": Gtk.ResponseType.CANCEL, "style": 'dialog-error'}
]
label = Gtk.Label()

View File

@ -141,9 +141,9 @@ class Panel(ScreenPanel):
vbox.add(label)
scroll.add(vbox)
recoverybuttons = [
{"name": _("Recover Hard"), "response": Gtk.ResponseType.OK},
{"name": _("Recover Soft"), "response": Gtk.ResponseType.APPLY},
{"name": _("Cancel"), "response": Gtk.ResponseType.CANCEL}
{"name": _("Recover Hard"), "response": Gtk.ResponseType.OK, "style": 'dialog-warning'},
{"name": _("Recover Soft"), "response": Gtk.ResponseType.APPLY, "style": 'dialog-info'},
{"name": _("Cancel"), "response": Gtk.ResponseType.CANCEL, "style": 'dialog-error'}
]
self._gtk.Dialog(_("Recover"), recoverybuttons, scroll, self.reset_confirm, program)
return
@ -206,8 +206,8 @@ class Panel(ScreenPanel):
scroll.add(vbox)
buttons = [
{"name": _("Update"), "response": Gtk.ResponseType.OK},
{"name": _("Cancel"), "response": Gtk.ResponseType.CANCEL}
{"name": _("Update"), "response": Gtk.ResponseType.OK, "style": 'dialog-info'},
{"name": _("Cancel"), "response": Gtk.ResponseType.CANCEL, "style": 'dialog-error'}
]
self._gtk.Dialog(_("Update"), buttons, scroll, self.update_confirm, program)
@ -321,9 +321,9 @@ class Panel(ScreenPanel):
vbox.add(label)
scroll.add(vbox)
buttons = [
{"name": _("Host"), "response": Gtk.ResponseType.OK},
{"name": _("Printer"), "response": Gtk.ResponseType.APPLY},
{"name": _("Cancel"), "response": Gtk.ResponseType.CANCEL}
{"name": _("Host"), "response": Gtk.ResponseType.OK, "style": 'dialog-info'},
{"name": _("Printer"), "response": Gtk.ResponseType.APPLY, "style": 'dialog-warning'},
{"name": _("Cancel"), "response": Gtk.ResponseType.CANCEL, "style": 'dialog-error'}
]
if method == "reboot":
title = _("Restart")

View File

@ -25,6 +25,7 @@ from ks_includes.files import KlippyFiles
from ks_includes.KlippyGtk import KlippyGtk
from ks_includes.printer import Printer
from ks_includes.widgets.keyboard import Keyboard
from ks_includes.widgets.prompts import Prompt
from ks_includes.config import KlipperScreenConfig
from panels.base_panel import BasePanel
@ -95,6 +96,7 @@ class KlipperScreen(Gtk.Window):
wayland = False
windowed = False
notification_log = []
prompt = None
def __init__(self, args):
try:
@ -769,7 +771,12 @@ class KlipperScreen(Gtk.Window):
self.panels['splash_screen'].check_power_status()
elif action == "notify_gcode_response" and self.printer.state not in ["error", "shutdown"]:
if not (data.startswith("B:") or data.startswith("T:")):
if data.startswith("echo: "):
if data.startswith("// action:"):
self.wake_screen()
if self.prompt is None:
self.prompt = Prompt(self)
self.prompt.decode(data[10:])
elif data.startswith("echo: "):
self.show_popup_message(data[6:], 1)
elif data.startswith("!! "):
self.show_popup_message(data[3:], 3)
@ -793,8 +800,8 @@ class KlipperScreen(Gtk.Window):
def _confirm_send_action(self, widget, text, method, params=None):
buttons = [
{"name": _("Accept"), "response": Gtk.ResponseType.OK},
{"name": _("Cancel"), "response": Gtk.ResponseType.CANCEL}
{"name": _("Accept"), "response": Gtk.ResponseType.OK, "style": 'dialog-info'},
{"name": _("Cancel"), "response": Gtk.ResponseType.CANCEL, "style": 'dialog-error'}
]
try:

View File

@ -292,18 +292,9 @@ trough {
.dialog button {
padding: 1.5em;
border-bottom: .4em solid @color3;
margin-top: 1em;
}
.dialog button:nth-child(1) {
border-bottom-color: @echo;
}
.dialog button:nth-child(2) {
border-bottom-color: @error;
}
.distbutton_active {
background-color: @active;
font-weight: 600;
@ -630,3 +621,28 @@ trough progress {
}
.graph_label {border-left-width: .4em; border-left-style: solid;}
.dialog-error {
border-bottom: .4em solid @error;
}
.dialog-warning {
border-bottom: .4em solid @warning;
}
.dialog-info {
border-bottom: .4em solid @color4;
}
.dialog-default {
border-bottom: .4em solid @color3;
}
.dialog-secondary {
border-bottom: .4em solid @color2;
}
.dialog-primary {
border-bottom: .4em solid @color1;
}

View File

@ -176,17 +176,9 @@ textview .time {
.dialog button {
background-color: @solarized-base02;
border-bottom-color: @solarized-cyan;
border-radius: .75em;
}
.dialog button:nth-child(1) {
border-bottom-color: @solarized-green;
}
.dialog button:nth-child(2) {
border-bottom-color: @solarized-red;
}
.tempbutton_top,
.tempbutton,
.tempbutton_bottom,
@ -267,3 +259,27 @@ textview .time {
.keyboard_pad {
background-color: @solarized-base02;
}
.dialog-error {
border-bottom-color: @solarized-red;
}
.dialog-warning {
border-bottom-color: @olarized-yellow;
}
.dialog-info {
border-bottom-color: @solarized-cyan;
}
.dialog-default {
border-bottom-color: @solarized-green;
}
.dialog-secondary {
border-bottom-color: @solarized-magenta;
}
.dialog-primary {
border-bottom-color: @solarized-orange;
}

View File

@ -159,18 +159,9 @@ textview .time {
.dialog button {
background-color: @buttons-bg;
border-bottom-color: @active-dark;
border-radius: .75em;
}
.dialog button:nth-child(1) {
border-bottom-color: @color3;
}
.dialog button:nth-child(2) {
border-bottom-color: @error;
}
.tempbutton_top,
.tempbutton,
.tempbutton_bottom,
@ -243,3 +234,27 @@ button.active {
.keyboard_pad {
background-color: @buttons-bg;
}
.dialog-error {
border-bottom-color: @error;
}
.dialog-warning {
border-bottom-color: @warning;
}
.dialog-info {
border-bottom-color: @color4;
}
.dialog-default {
border-bottom-color: @color3;
}
.dialog-secondary {
border-bottom-color: @color2;
}
.dialog-primary {
border-bottom-color: @color1;
}

View File

@ -153,18 +153,9 @@ textview .time {
.dialog button {
background-color: #252525;
border-bottom-color: #252525;
border-radius: .25em;
}
.dialog button:nth-child(1) {
border-bottom-color: #2f5631;
}
.dialog button:nth-child(2) {
border-bottom-color: #B71C1C;
}
.tempbutton_top,
.tempbutton,
.tempbutton_bottom,
@ -242,3 +233,27 @@ textview .time {
.keyboard_pad {
background-color: #090909;
}
.dialog-error {
border-bottom-color: #CF6679;
}
.dialog-warning {
border-bottom-color: #f9b274;
}
.dialog-info {
border-bottom-color: @color4;
}
.dialog-default {
border-bottom-color: @color3;
}
.dialog-secondary {
border-bottom-color: @color2;
}
.dialog-primary {
border-bottom-color: @color1;
}

View File

@ -11,7 +11,6 @@
@define-color error #EF9A9A;
@define-color echo #a1ef9a;
@define-color popup #006064;
@define-color dialog #80CBC4;
@define-color textview #212121;
* {
@ -206,27 +205,6 @@ textview .time {
background-color: @bg;
}
.dialog button {
background-color: @dialog;
border-bottom-color: @dialog;
border-radius: .75em;
}
.dialog button:nth-child(1) {
background-color: @color3;
border-bottom-color: @color3;
}
.dialog button:nth-child(2) {
background-color: @error;
border-bottom-color: @error;
}
.dialog button:nth-child(1) label ,
.dialog button:nth-child(2) label {
color: @text;
}
.tempbutton_top,
.tempbutton,
.tempbutton_bottom,
@ -307,3 +285,37 @@ textview .time {
.keyboard_pad {
background-color: @border;
}
.dialog button {
border-radius: .75em;
}
.dialog-error {
background-color: @error;
border-bottom-color: @error;
}
.dialog-warning {
background-color: @warning;
border-bottom-color: @warning;
}
.dialog-info {
background-color: @color4;
border-bottom-color: @color4;
}
.dialog-default {
background-color: @color3;
border-bottom-color: @color3;
}
.dialog-secondary {
background-color: @color2;
border-bottom-color: @color2;
}
.dialog-primary {
background-color: @color1;
border-bottom-color: @color1;
}

View File

@ -5,6 +5,7 @@
@define-color bg #13181C;
@define-color active #324A5E;
@define-color error #981E1F;
@define-color warning #f9a825;
@define-color text white;
@define-color text-inv black;
@define-color lines #cccccc;
@ -135,18 +136,6 @@ textview .time {
background-color: @bg;
}
.dialog button {
border-bottom-color: @color3;
}
.dialog button:nth-child(1) {
border-bottom-color: @color4;
}
.dialog button:nth-child(2) {
border-bottom-color: @error;
}
.distbutton_active {
background-color: @active;
border-color: @color1;
@ -203,3 +192,27 @@ textview .time {
.keyboard_pad {
background-color: @bg;
}
.dialog-error {
border-bottom-color: @error;
}
.dialog-warning {
border-bottom-color: @warning;
}
.dialog-info {
border-bottom-color: @color4;
}
.dialog-default {
border-bottom-color: @color3;
}
.dialog-secondary {
border-bottom-color: @color2;
}
.dialog-primary {
border-bottom-color: @color1;
}