[PATCH 2/3] Use a separate label for passphrase warnings.

David Shea dshea at redhat.com
Fri May 9 20:19:21 UTC 2014


The GtkEntry secondary icon is already used for the builtin caps lock
check, so our passphrase checks were running over that and sometimes
each other and in all not working the best depending on which error went
active when. Moved the error messages into an image and label widget
separate from the GtkEntry, and converted the checks to InputCheck to
keep them all straight.
---
 pyanaconda/ui/gui/spokes/lib/passphrase.glade | 51 +++++++++++---
 pyanaconda/ui/gui/spokes/lib/passphrase.py    | 98 +++++++++++++++++----------
 2 files changed, 104 insertions(+), 45 deletions(-)

diff --git a/pyanaconda/ui/gui/spokes/lib/passphrase.glade b/pyanaconda/ui/gui/spokes/lib/passphrase.glade
index ac1b904..05e3354 100644
--- a/pyanaconda/ui/gui/spokes/lib/passphrase.glade
+++ b/pyanaconda/ui/gui/spokes/lib/passphrase.glade
@@ -117,7 +117,6 @@
                 <property name="width_chars">32</property>
                 <signal name="activate" handler="on_entry_activated" swapped="no"/>
                 <signal name="changed" handler="on_passphrase_changed" swapped="no"/>
-                <signal name="focus-out-event" handler="on_passphrase_editing_done" swapped="no"/>
               </object>
               <packing>
                 <property name="left_attach">1</property>
@@ -135,7 +134,7 @@
               </object>
               <packing>
                 <property name="left_attach">0</property>
-                <property name="top_attach">2</property>
+                <property name="top_attach">3</property>
               </packing>
             </child>
             <child>
@@ -146,12 +145,10 @@
                 <property name="invisible_char">●</property>
                 <property name="width_chars">32</property>
                 <signal name="activate" handler="on_entry_activated" swapped="no"/>
-                <signal name="changed" handler="on_confirm_changed" swapped="no"/>
-                <signal name="focus-out-event" handler="on_confirm_editing_done" swapped="no"/>
               </object>
               <packing>
                 <property name="left_attach">1</property>
-                <property name="top_attach">2</property>
+                <property name="top_attach">3</property>
               </packing>
             </child>
             <child>
@@ -187,7 +184,7 @@
               </object>
               <packing>
                 <property name="left_attach">1</property>
-                <property name="top_attach">1</property>
+                <property name="top_attach">2</property>
               </packing>
             </child>
             <child>
@@ -200,7 +197,45 @@
               </object>
               <packing>
                 <property name="left_attach">0</property>
+                <property name="top_attach">2</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkBox" id="passphrase_warning_box">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="spacing">6</property>
+                <child>
+                  <object class="GtkImage" id="passphrase_warning_image">
+                    <property name="can_focus">False</property>
+                    <property name="halign">center</property>
+                    <property name="stock">gtk-missing-image</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="passphrase_warning_label">
+                    <property name="can_focus">False</property>
+                    <property name="hexpand">True</property>
+                    <property name="xalign">0</property>
+                    <property name="wrap">True</property>
+                    <property name="max_width_chars">1</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
                 <property name="top_attach">1</property>
+                <property name="width">2</property>
               </packing>
             </child>
           </object>
@@ -211,7 +246,7 @@
           </packing>
         </child>
         <child>
-          <object class="GtkBox" id="warning_box">
+          <object class="GtkBox" id="keymap_warning_box">
             <property name="visible">True</property>
             <property name="can_focus">False</property>
             <property name="spacing">6</property>
@@ -228,7 +263,7 @@
               </packing>
             </child>
             <child>
-              <object class="GtkLabel" id="passphrase_warning_label">
+              <object class="GtkLabel" id="keymap_warning_label">
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
                 <property name="label" translatable="yes">Warning: You won't be able to switch between keyboard layouts (from the default one) when you decrypt your disks after install.</property>
diff --git a/pyanaconda/ui/gui/spokes/lib/passphrase.py b/pyanaconda/ui/gui/spokes/lib/passphrase.py
index 197ee13..169d2e8 100644
--- a/pyanaconda/ui/gui/spokes/lib/passphrase.py
+++ b/pyanaconda/ui/gui/spokes/lib/passphrase.py
@@ -23,7 +23,9 @@ from gi.repository import Gtk, Gdk
 
 import pwquality
 
+from pyanaconda.ui.helpers import InputCheck
 from pyanaconda.ui.gui import GUIObject
+from pyanaconda.ui.gui.helpers import GUIInputCheckHandler
 from pyanaconda.constants import PW_ASCII_CHARS
 from pyanaconda.i18n import _, N_
 
@@ -32,13 +34,14 @@ __all__ = ["PassphraseDialog"]
 ERROR_WEAK = N_("You have provided a weak passphrase: %s")
 ERROR_NOT_MATCHING = N_("Passphrases do not match.")
 
-class PassphraseDialog(GUIObject):
+class PassphraseDialog(GUIObject, GUIInputCheckHandler):
     builderObjects = ["passphrase_dialog"]
     mainWidgetName = "passphrase_dialog"
     uiFile = "spokes/lib/passphrase.glade"
 
     def __init__(self, data):
         GUIObject.__init__(self, data)
+        GUIInputCheckHandler.__init__(self)
 
         self._confirm_entry = self.builder.get_object("confirm_pw_entry")
         self._passphrase_entry = self.builder.get_object("passphrase_entry")
@@ -48,6 +51,9 @@ class PassphraseDialog(GUIObject):
         self._strength_bar = self.builder.get_object("strength_bar")
         self._strength_label = self.builder.get_object("strength_label")
 
+        self._passphrase_warning_image = self.builder.get_object("passphrase_warning_image")
+        self._passphrase_warning_label = self.builder.get_object("passphrase_warning_label")
+
         # Set the offset values for the strength bar colors
         self._strength_bar.add_offset_value("low", 2)
         self._strength_bar.add_offset_value("medium", 3)
@@ -58,13 +64,17 @@ class PassphraseDialog(GUIObject):
         self._pwq_error = None
         self.passphrase = ""
 
+        self._passphrase_match_check = self.add_check(self._passphrase_entry, self._checkMatch)
+        self._confirm_match_check = self.add_check(self._confirm_entry, self._checkMatch)
+        self._strength_check = self.add_check(self._passphrase_entry, self._checkStrength)
+        self._ascii_check = self.add_check(self._passphrase_entry, self._checkASCII)
+
     def refresh(self):
         super(PassphraseDialog, self).refresh()
 
         # disable input methods for the passphrase Entry widgets and make sure
         # the focus change mask is enabled
         self._passphrase_entry.set_property("im-module", "")
-        self._passphrase_entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, None)
         self._passphrase_entry.add_events(Gdk.EventMask.FOCUS_CHANGE_MASK)
         self._confirm_entry.set_property("im-module", "")
         self._confirm_entry.add_events(Gdk.EventMask.FOCUS_CHANGE_MASK)
@@ -84,6 +94,12 @@ class PassphraseDialog(GUIObject):
 
         self._update_passphrase_strength()
 
+        # Update the check states and force a status update
+        self._passphrase_match_check.update_check_status()
+        self._strength_check.update_check_status()
+        self._ascii_check.update_check_status()
+        self.set_status(None)
+
     def run(self):
         self.refresh()
         self.window.show_all()
@@ -116,55 +132,63 @@ class PassphraseDialog(GUIObject):
         self._strength_bar.set_value(val)
         self._strength_label.set_text(text)
 
-    def _set_entry_icon(self, entry, icon, msg):
-        entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, icon)
-        entry.set_icon_tooltip_text(Gtk.EntryIconPosition.SECONDARY, msg)
+    def set_status(self, inputcheck):
+        # Set the warning message with the result from the first failed check
+        failed_check = next(self.failed_checks_with_message, None)
 
-    def on_passphrase_changed(self, entry):
-        self._update_passphrase_strength()
-        passphrase = entry.get_text()
-        if passphrase and passphrase == self._confirm_entry.get_text():
-            self._set_entry_icon(self._confirm_entry, None, "")
+        if failed_check:
+            result_icon, result_message = failed_check.check_status
+            self._passphrase_warning_image.set_from_icon_name(result_icon, Gtk.IconSize.BUTTON)
+            self._passphrase_warning_label.set_text(result_message)
+            self._passphrase_warning_image.set_visible(True)
+            self._passphrase_warning_label.set_visible(True)
+        else:
+            self._passphrase_warning_image.set_visible(False)
+            self._passphrase_warning_label.set_visible(False)
+
+        # The save button should only be sensitive if the match check passes
+        if self._passphrase_match_check.check_status == InputCheck.CHECK_OK and \
+                self._confirm_match_check.check_status == InputCheck.CHECK_OK:
             self._save_button.set_sensitive(True)
         else:
             self._save_button.set_sensitive(False)
 
-        if not self._pwq_error:
-            self._set_entry_icon(entry, None, "")
+    def _checkASCII(self, inputcheck):
+        passphrase = self.get_input(inputcheck.input_obj)
 
-        can_override_icon = entry.get_icon_name(Gtk.EntryIconPosition.SECONDARY)\
-                            in ["gtk-dialog-warning", "", None]
-        if can_override_icon:
-            # no other icon set, make non-ASCII warning (lowest priority) effective
-            if passphrase and any(char not in PW_ASCII_CHARS for char in passphrase):
-                self._set_entry_icon(entry, "gtk-dialog-warning",
-                                     _("contains non-ASCII characters"))
-            else:
-                self._set_entry_icon(entry, None, "")
+        if passphrase and any(char not in PW_ASCII_CHARS for char in passphrase):
+            return ("gtk-dialog-warning", _("Passphrase contains non-ASCII characters"))
+        else:
+            return InputCheck.CHECK_OK
 
-    def on_passphrase_editing_done(self, entry, *args):
+    def _checkStrength(self, inputcheck):
         if self._pwq_error:
-            icon = "gtk-dialog-error"
-            msg = _(ERROR_WEAK) % self._pwq_error
-            self._set_entry_icon(entry, icon, msg)
-
-    def on_confirm_changed(self, entry):
-        if entry.get_text() and entry.get_text() == self._passphrase_entry.get_text():
-            self._set_entry_icon(entry, None, "")
-            self._save_button.set_sensitive(True)
+            return ("gtk-dialog-error", _(ERROR_WEAK) % self._pwq_error)
         else:
-            self._save_button.set_sensitive(False)
+            return InputCheck.CHECK_OK
 
-    def on_confirm_editing_done(self, entry, *args):
+    def _checkMatch(self, inputcheck):
         passphrase = self._passphrase_entry.get_text()
         confirm = self._confirm_entry.get_text()
         if passphrase != confirm:
-            icon = "gtk-dialog-error"
-            msg = ERROR_NOT_MATCHING
-            self._set_entry_icon(entry, icon, _(msg))
-            self._save_button.set_sensitive(False)
+            result = ("gtk-dialog-error", _(ERROR_NOT_MATCHING))
         else:
-            self._set_entry_icon(entry, None, "")
+            result = InputCheck.CHECK_OK
+
+        # If the check succeeded, reset the status of the other check object
+        # Disable the current check to prevent a cycle
+        if result == InputCheck.CHECK_OK:
+            inputcheck.enabled = False
+            if inputcheck == self._passphrase_match_check:
+                self._confirm_match_check.update_check_status()
+            else:
+                self._passphrase_match_check.update_check_status()
+            inputcheck.enabled = True
+
+        return result
+
+    def on_passphrase_changed(self, entry):
+        self._update_passphrase_strength()
 
     def on_save_clicked(self, button):
         self.passphrase = self._passphrase_entry.get_text()
-- 
1.9.0



More information about the anaconda-patches mailing list