show notification when copying to clipboard
Florian Walbroel

Florian Walbroel commited on 2021-09-02 15:50:45
Showing 2 changed files, with 120 additions and 9 deletions.

... ...
@@ -55,6 +55,10 @@
55 55
     <child>
56 56
       <placeholder/>
57 57
     </child>
58
+    <child>
59
+      <object class="GtkOverlay">
60
+        <property name="visible">True</property>
61
+        <property name="can_focus">False</property>
58 62
         <child>
59 63
           <object class="GtkBox" id="TopVBox">
60 64
             <property name="visible">True</property>
... ...
@@ -334,6 +338,67 @@
334 338
               </packing>
335 339
             </child>
336 340
           </object>
341
+          <packing>
342
+            <property name="index">-1</property>
343
+          </packing>
344
+        </child>
345
+        <child type="overlay">
346
+          <object class="GtkRevealer" id="NotificationRevealer">
347
+            <property name="visible">True</property>
348
+            <property name="can_focus">False</property>
349
+            <property name="halign">center</property>
350
+            <property name="valign">start</property>
351
+            <property name="transition_type">slide-up</property>
352
+            <child>
353
+              <object class="GtkBox">
354
+                <property name="visible">True</property>
355
+                <property name="can_focus">False</property>
356
+                <property name="halign">center</property>
357
+                <property name="valign">start</property>
358
+                <property name="spacing">6</property>
359
+                <child>
360
+                  <object class="GtkLabel" id="NotificationMessage">
361
+                    <property name="visible">True</property>
362
+                    <property name="can_focus">False</property>
363
+                    <property name="label" translatable="yes">Sample notification message to be
364
+shown to the user</property>
365
+                    <property name="justify">center</property>
366
+                  </object>
367
+                  <packing>
368
+                    <property name="expand">False</property>
369
+                    <property name="fill">True</property>
370
+                    <property name="position">1</property>
371
+                  </packing>
372
+                </child>
373
+                <child>
374
+                  <object class="GtkButton" id="NotificationClose">
375
+                    <property name="visible">True</property>
376
+                    <property name="can_focus">True</property>
377
+                    <property name="receives_default">True</property>
378
+                    <property name="relief">none</property>
379
+                    <signal name="clicked" handler="onNotificationClose" swapped="no"/>
380
+                    <child>
381
+                      <object class="GtkImage">
382
+                        <property name="visible">True</property>
383
+                        <property name="can_focus">False</property>
384
+                        <property name="icon_name">window-close-symbolic</property>
385
+                      </object>
386
+                    </child>
387
+                  </object>
388
+                  <packing>
389
+                    <property name="expand">False</property>
390
+                    <property name="fill">True</property>
391
+                    <property name="position">2</property>
392
+                  </packing>
393
+                </child>
394
+                <style>
395
+                  <class name="app-notification"/>
396
+                </style>
397
+              </object>
398
+            </child>
399
+          </object>
400
+        </child>
401
+      </object>
337 402
     </child>
338 403
   </object>
339 404
 </interface>
... ...
@@ -19,7 +19,7 @@ import gi
19 19
 gi.require_version('Gdk', '3.0')
20 20
 gi.require_version('Gtk', '3.0')
21 21
 # pylint: disable=C0413
22
-from gi.repository import Gdk, Gtk, GLib
22
+from gi.repository import Gdk, Gtk, GLib, GObject
23 23
 
24 24
 GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, Gtk.main_quit)
25 25
 
... ...
@@ -192,12 +192,16 @@ class UptGui:
192 192
         self.wid_processes_tree = self.builder.get_object('ProcessesTree')
193 193
         self.wid_processes_view = self.builder.get_object('ProcessesView')
194 194
         self.wid_tree_toggle = self.builder.get_object('TreeToggle')
195
+        self.notifier = self.builder.get_object("NotificationRevealer")
196
+        self.notifier_msg = self.builder.get_object("NotificationMessage")
197
+        self.notifier_timeout = None
195 198
         handlers = {
196 199
             'onDestroy': self.onDestroy,
197 200
             'onDetailsRowActivated': self.onDetailsRowActivated,
198 201
             'onProcessesCursorChanged': self.onProcessesCursorChanged,
199 202
             'onProcessesRowActivated': self.onProcessesRowActivated,
200
-            'onTreeToggled': self.onTreeToggled
203
+            'onTreeToggled': self.onTreeToggled,
204
+            'onNotificationClose': self.onNotificationClose,
201 205
         }
202 206
         self.builder.connect_signals(handlers)
203 207
         # open trace file
... ...
@@ -281,8 +285,7 @@ class UptGui:
281 285
                                                     self.DETAIL_VALUE))
282 286
                 child_iter = self.wid_details_tree.iter_next(child_iter)
283 287
             string = cmdline2str(strings)
284
-        self.clipboard.set_text(string, -1)
285
-        self.clipboard.store()
288
+        self.storeInClipboardAndNotify(string)
286 289
         # get proc_id of selected row, nothing else to do if none
287 290
         proc_id = self.wid_details_tree.get_value(detail_iter,
288 291
                                                   self.DETAIL_PROC_ID)
... ...
@@ -340,8 +343,7 @@ class UptGui:
340 343
         if proc.cmdline:
341 344
             string += ' ' + cmdline2str(proc.cmdline)
342 345
         string += ' )'
343
-        self.clipboard.set_text(string, -1)
344
-        self.clipboard.store()
346
+        self.storeInClipboardAndNotify(string)
345 347
 
346 348
     def onTreeToggled(self, _widget):
347 349
         """
... ...
@@ -352,6 +354,51 @@ class UptGui:
352 354
         # re-populate processes view
353 355
         self.populateProcesses()
354 356
 
357
+    def onNotificationClose(self, _widget):
358
+        """
359
+        Notification close button pressed: close the notification
360
+        """
361
+        self.closeNotification()
362
+
363
+    def closeNotification(self):
364
+        """
365
+        Closes the notification and kills the timeout if set
366
+        """
367
+        self.notifier.set_reveal_child(False)
368
+        if self.notifier_timeout is not None:
369
+            GObject.source_remove(self.notifier_timeout)
370
+            self.notifier_timeout = None
371
+
372
+    def showNotification(self, message: str, timeout_ms: int = None):
373
+        """
374
+        Shows a notification with the given message text, if timeout (in ms)
375
+        is provided also sets a timeout to close the notification
376
+        """
377
+        self.closeNotification()
378
+
379
+        self.notifier_msg.set_text(message)
380
+        self.notifier.set_reveal_child(True)
381
+
382
+        if timeout_ms is not None:
383
+            self.notifier_timeout = GObject.timeout_add(
384
+                timeout_ms, self.closeNotification)
385
+
386
+    def storeInClipboard(self, string: str):
387
+        """
388
+        Stores a string in the clipboard
389
+        """
390
+        self.clipboard.set_text(string, -1)
391
+        self.clipboard.store()
392
+
393
+    def storeInClipboardAndNotify(self, string: str):
394
+        """
395
+        Stores a string in the clipboard and shows a "copied to clipboard"
396
+        notification
397
+        """
398
+        self.storeInClipboard(string)
399
+        msg = string if len(string) <= 100 else string[:97] + "..."
400
+        self.showNotification(f"'{msg:s}'\nCopied to clipboard", 1000)
401
+
355 402
     def openTrace(self, proto_filename: str):
356 403
         """
357 404
         Open a trace file.
... ...
@@ -512,9 +559,8 @@ class UptGui:
512 559
             for i, child_proc in enumerate(child_procs):
513 560
                 child_iter = add(f'child {i:d}',
514 561
                                  cmdline2str(child_proc.cmdline), list_iter)
515
-                self.wid_details_tree.set_value(child_iter,
516
-                                                self.DETAIL_PROC_ID,
517
-                                                child_proc.proc_id)
562
+                self.wid_details_tree.set_value(
563
+                    child_iter, self.DETAIL_PROC_ID, child_proc.proc_id)
518 564
             self.wid_details_view.expand_row(
519 565
                 self.wid_details_tree.get_path(list_iter), True)
520 566
 
521 567