Gtk Color Chooser - Color Select from Screen - doesnt work - Gtk version 3.24.33

44 Views Asked by At

I was drawing on Linux Mint 21 with application drawing - version 1.02 and noticed, that the color select tool
( look at https://github.com/maoschanz/drawing/issues/630 ) is not working.
I cant select any color from screen...
This is a Gtk Widget - Gtk.ColorChooserWidget!

I examined it, using GJS, and checked, if there is any handler pending on the Color-Select button!

Gtk.ColorChooserWidget test:

#!/usr/bin/gjs
"use strict";
imports.gi.versions.Gtk="3.0";
const { Gtk, GObject }= imports.gi;
Gtk.init(null);
const window = new Gtk.Window(), colorChooser= new Gtk.ColorChooserWidget ();

window.set_title("ColorChooserWidget test");
window.connect('destroy', () => { Gtk.main_quit(); });
window.set_size_request(640, 480);
window.add (colorChooser);
window.show_all();

log ("Gtk Version: "+Gtk.get_major_version()+"."+Gtk.get_minor_version()+"."+Gtk.get_micro_version());

//-- Gtk.ColorChooserWidget has only button_colorSelect !!
var button_select, button_cancel, button_colorSelect;
function find_buttons (gtk_children)                                                //-- finds the Color Chooser Dialog buttons Color-Select|Cancel|Select
   {
   const l=gtk_children.length;
   var i, gtk_child, button_child;
   for (i=0;i<l;i++)
      {
      gtk_child=gtk_children[i];
      if (gtk_child.get_children) find_buttons (gtk_child.get_children());
      if (gtk_child.constructor === Gtk.Button)
         {
         button_child=gtk_child.get_children()[0];
         if (button_child.constructor === Gtk.Image) button_colorSelect=gtk_child;  //-- button with image -> colorSelect button
         else                                                                       //-- button with label -> Select or Cancel
            {
            if (button_child.get_text() == "Select") button_select=gtk_child;
            else button_cancel=gtk_child;
            }
         }
      } 
   }
find_buttons (colorChooser.get_children());

var signal_ID=GObject.signal_lookup ("button_press_event", Gtk.Button.$gtype);
log ("class Gtk.Button - 'button-press-event' signal ID :"+signal_ID);

//-- GObject.signal_has_handler_pending ([GObject object OBJ], [signal ID NUM(int)], [detail GQuark-NUM(int)], [include blocked handlers BOOL]), for non detailed signal 0 can be used as GQuark
log ("button_colorSelect has handler pending on 'button-press-event' :" +GObject.signal_has_handler_pending (button_colorSelect, signal_ID, 0, true));

//-- test, if the button colorSelect is correctly identified - click on it
button_colorSelect.connect ("button-press-event", function () { log ("button colorSelect"); });

Gtk.main();

output:

Gjs-Message: 23:13:23.831: JS LOG: Gtk Version: 3.24.33
Gjs-Message: 23:13:23.833: JS LOG: class Gtk.Button - 'button-press-event' signal ID :66
Gjs-Message: 23:13:23.833: JS LOG: button_colorSelect has handler pending on 'button-press-event' :false

The Color Select button doesnt have any handler pending!!, so its without functionality! Is that only on Linux Mint ???
Or is it generally on other platforms, and you have to program the functionality yourself?

1

There are 1 best solutions below

0
Schmaehgrunza On

this is not an answer to my question, but a workaround.

I've appended this functionality, for GJS users, if you want to use
CLASS interface_ColorChooser

  • holding shift, while clicking, multiple selection
  • pressing STRG -- cancel
#!/usr/bin/gjs
"use strict";
imports.gi.versions.Gtk="3.0";
const { Gtk, Gdk, GdkPixbuf, GObject } = imports.gi;
Gtk.init(null);

const window = new Gtk.Window(), box=new Gtk.Box ({ orientation:1 }), button_startDialog=new Gtk.Button ({label: "get color dialog"}),
      labelA= new Gtk.Label ({ label: "Gtk.ColorChooserDialog", margin_top: 10, margin_bottom: 10 }), labelB= new Gtk.Label ({ label: "Gtk.ColorButton", margin_top: 10, margin_bottom: 10  }), 
      labelC= new Gtk.Label ({ label: "Gtk.ColorChooserWidget", margin_top: 10, margin_bottom: 10 });
      
var colorChooserA, colorChooserB, colorChooserC;


window.set_title("interface_ColorChooser");
window.connect('destroy', () => { Gtk.main_quit(); });
window.set_size_request(640, 480);
window.add (box);

/* ========================================================================================================================================
   CLASS interface_ColorChooser (implementation)
   ========================================================================================================================================
   implementation ... an instance of the three Color Chooser implementations Gtk.ColorButton, Gtk.ColorChooserWidget, Gtk.ColorChooserDialog */
{
var interface_ColorChooser=function (implementation)
   {
   this.implementation=implementation;     //-- three kinds of interface implementations -> Gtk.ColorButton, Gtk.ColorChooserWidget, Gtk.ColorChooserDialog
   this.button_select=null;                //-- Color Chooser Dialog button Select
   this.button_cancel=null;                //-- Color Chooser Dialog button Cancel
   this.button_colorSelect=null;           //-- Color Chooser Widget button Color-Select 
   this.button_colorSelect_handlerID=null; //-- ID for the handler connected to the button_colorSelect at "button-press-event" 
   this.average_33=true;                   //-- average color of a 3*3 pixel field under pointer

   //-- cursor for color selection - check hotspot values for new cursors!
   this.cursor=Gdk.Cursor.new_from_pixbuf (display, Gtk.IconTheme.get_default().load_icon ("color-select", 3, Gtk.IconLookupFlags.FORCE_SVG), 6, 16);
   this.colorButton_CCW=null;              //-- For implementation Gtk.ColorButton, the property holds a reference to the connected Gtk.ColorChooserWidget
   this.colorButton_handlerID=null;        //--                                     ID of the handler connected to the ColorButton at "butten-press-event" signal
   }

//-- destroys the interface and the implementation without memory leaks
interface_ColorChooser.prototype.destroy=function (dont_destroy_implementation)
   {
   this.button_select=null; this.button_cancel=null;
   if (this.button_colorSelect_handlerID) this.button_colorSelect.disconnect (this.button_colorSelect_handlerID);
   this.button_colorSelect=null; this.cursor=null;

   this.colorButton_CCW=null;
   if (this.colorButton_handlerID) this.implementation.disconnect (this.colorButton_handlerID);
   if (!dont_destroy_implementation) this.implementation.destroy(); this.implementation=null;
   }

/* connects to the Color Chooser implementation
   means -> finds the buttons Color-Select|Cancel|Select, if they exist and then add handler for color selecting to Color-Select button
   exception: Gtk.ColorButton -> if button_colorSelect isnt found yet,
              adds a handler "find_colorButton_dialog" at signal "button-press-event" on ColorButton instance, if not set yet */
interface_ColorChooser.prototype.connect=function ()
   {
   if (!this.button_colorSelect)
      {
      if (this.implementation.constructor === Gtk.ColorButton)
         {
         //-- add handler "find_colorButton_dialog" to Gtk.ColorButton instance at signal "button-press-event"
         if (!this.colorButton_handlerID)
            this.colorButton_handlerID=this.implementation.connect("button-press-event", find_colorButton_dialog.bind(this));
         else return;
         }
      else this.find_buttons (this.implementation.get_children());
      }
   
   //-- add signal handler for color selecting to Color-Select button
   if (this.button_colorSelect && this.button_colorSelect_handlerID==null)
      this.button_colorSelect_handlerID=this.button_colorSelect.connect ("button-press-event", pointer_grab.bind(this));  
   }

//-- finds the Color Chooser Dialog buttons Color-Select|Cancel|Select, if they exist
interface_ColorChooser.prototype.find_buttons=function (gtk_children)
   {
   const l=gtk_children.length;
   var i, gtk_child, button_child;
   for (i=0;i<l;i++)
      {
      gtk_child=gtk_children[i];
      if (gtk_child.get_children) this.find_buttons (gtk_child.get_children());
      if (gtk_child.constructor === Gtk.Button)
         {
         button_child=gtk_child.get_children()[0];
         if (button_child.constructor === Gtk.Image) this.button_colorSelect=gtk_child;  //-- button with image -> colorSelect button
         else                                                                            //-- button with label -> Select or Cancel
            {
            if (button_child.get_text() == "Select") this.button_select=gtk_child;
            else this.button_cancel=gtk_child;
            }
         }
      } 
   }

let root_window=Gdk.get_default_root_window(), //-- root Window
    display=Gdk.Display.get_default(),         //-- default Display
    seat=display.list_seats()[0],              //-- collection of input devices that belong to a user
    colorSelecting=false, colorPicked=false, colorChooser_inAction=null,
    keyStack=null, handler_register=new Object (), button_down_lastTimeStamp=null,


//-- template function for bind - will be bound to interface_ColorChooser instance
//-- called from "find_colorButton_dialog" setTimeout
find_colorButton_dialog_timeout=function ()
   {
   let i, windows=Gtk.Window.list_toplevels(), identification;
   for (i=0; i<windows.length;i++)
      {
      if (windows[i]!==window)
         {
         try { identification=windows[i].get_children()[0].get_children()[0]; }
         catch (error) { continue; }
         if (identification.constructor === Gtk.ColorChooserWidget)
            {
            this.colorButton_CCW=identification;
            this.find_buttons (windows[i].get_children()); //-- start search in window, to find also dialog buttons Cancel, Select, CCW has only Color-Select button!
            this.connect();
            break;
            }
         }
      }
   },

//-- template function for bind - will be bound to interface_ColorChooser instance
//-- signal handler for "button-press-event" on Gtk.ColorButton instance
find_colorButton_dialog=function (button, event)
   {
   if (this.colorButton_CCW) return;                          //-- already found
   setTimeout(find_colorButton_dialog_timeout.bind(this), 300);  //-- can only be found, if ColorButton press action has opened the dialog, wait 300ms after button press
   },
   
//-- template function for bind - will be bound to interface_ColorChooser instance
//-- signal handler for "button-press-event" on Color Chooser Widget button Color-Select
pointer_grab=function (button, event)
   {
   seat.grab (window.get_window(), Gdk.SeatCapabilities.POINTER | Gdk.SeatCapabilities.KEYBOARD, false, this.cursor, null, null); //
   colorChooser_inAction=this; colorSelecting=true; colorPicked=false; keyStack=[]; button_down_lastTimeStamp=-1;
   },

//-- signal handler for window "button-press-event"
onColorSelecting=function (window, event)
   {
   if (colorSelecting && !colorPicked)
      {
      const root_coords=seat.get_pointer().get_position ();
      let pixbuf, pixels, u, i, l, d, red_sum=0, green_sum=0, blue_sum=0,
          red, green, blue, alpha;
      
      if (event.get_time()-button_down_lastTimeStamp<=2) return;           //-- if SHIFT is hold down, then one mouse down causes two button press events, one immediateley after the other
      else button_down_lastTimeStamp=event.get_time();                     //--             dont know why, but can eliminate that with time check.
      
      if (colorChooser_inAction.average_33)                                                  //-- 3*3 field: build average values
         {
         pixbuf=Gdk.pixbuf_get_from_window (root_window, root_coords[1]-1, root_coords[2]-1, 3, 3);
         pixels=pixbuf.get_pixels();
         
         if (pixbuf.get_has_alpha()) { d=4; alpha=pixels[19]/255; }  //-- d .. count of values for one pixel, alpha from the middle point
         else { d=3; alpha=1 };
         for (u=0, i=0; u<3; u++)     //-- 3 lines
            {
            for (l=i+3*d; i<l; i+=d)  //-- 3 columns
               {
               red_sum+=pixels[i]; green_sum+=pixels[i+1]; blue_sum+=pixels[i+2];
               }

            if (d==3) i+=d; //-- internal inside pixbuf no alpha nulls are added per line to the pixel data,
            }               //-- some kind of padding? 3 pixel 9 color values plus 3 nulls are 12 values per line

         red=red_sum/2295; green=green_sum/2295; blue=blue_sum/2295; //-- 9*255=2295
         }
      else                                                                                  //-- 1*1 field
         {
         pixbuf=Gdk.pixbuf_get_from_window (root_window, root_coords[1], root_coords[2], 1, 1);
         pixels=pixbuf.get_pixels();
         if (pixbuf.get_has_alpha()) alpha=pixels[3]/255;
         else alpha=1;
         red=pixels[0]/255; green=pixels[1]/255; blue=pixels[2]/255;
         }
      
      colorChooser_inAction.implementation.set_rgba (new Gdk.RGBA ({ red: red, green: green, blue: blue, alpha: alpha }));
      if (colorChooser_inAction.colorButton_CCW) colorChooser_inAction.colorButton_CCW.set_rgba (new Gdk.RGBA ({ red: red, green: green, blue: blue, alpha: alpha }));
      if (!keyStack.includes(50)) colorPicked=true;                                         //-- pressed SHIFT will keep colorSelecting
      }
   },

//-- signal handler for window "button-release-event"   
pointer_ungrab=function (window, event)
   {
   if (colorSelecting && colorPicked)
      {
      seat.ungrab ();
      colorSelecting=false; colorPicked=false; colorChooser_inAction=null;
      }
   },

//-- signal handler for window "key-press-event"
onkeypress=function (window, event)
   { 
   if (colorSelecting)
      {
      const keyCode=event.get_keycode();
      if (keyCode[0])
         {
         if (keyCode[1] == 37) { colorPicked=true; pointer_ungrab (); }                     //-- pressed STRG will cancel colorSelecting
         if (keyStack.length==0 || keyCode[1]!=keyStack[keyStack.length-1]) keyStack.push(keyCode[1]);
         }
      }
   },

//-- signal handler for window "key-release-event"
onkeyrelease=function (window, event)
   {
   if (colorSelecting)
      {
      const keyCode=event.get_keycode();
      var i;
      
      if (keyCode[0])
         {
         for (i=0; i<keyStack.length; i++) { if (keyStack[i] == keyCode[1]) { keyStack.splice(i,1); break; } }
         }
      }
   };

handler_register.window=[];
handler_register.window.push (window.connect ("key-press-event",      onkeypress));
handler_register.window.push (window.connect ("key-release-event",    onkeyrelease));
handler_register.window.push (window.connect ("button-press-event",   onColorSelecting));
handler_register.window.push (window.connect ("button-release-event", pointer_ungrab));
}
/* ========================================================================================================================================
   CLASS interface_ColorChooser (implementation)
   ======================================================================================================================================== */

box.add(labelA);
box.add(button_startDialog);

//-- Gtk Color Chooser Dialog
button_startDialog.connect ("button-press-event", function (button, event)
   {
   if (!colorChooserA)
      {
      colorChooserA=new interface_ColorChooser (new Gtk.ColorChooserDialog());
      colorChooserA.connect();
      colorChooserA.implementation.show();
      colorChooserA.implementation.connect ("destroy", function () { colorChooserA.destroy(true); colorChooserA=null; });
      }
   });
   
box.add(new Gtk.Separator ({ orientation:0, margin_top: 10, margin_bottom: 10 }));

//-- Gtk Color Button
box.add(labelB);
colorChooserB=new interface_ColorChooser (new Gtk.ColorButton());
colorChooserB.connect();
box.add (colorChooserB.implementation);
box.add(new Gtk.Separator ({ orientation:0, margin_top: 10, margin_bottom: 10 }));

//-- Gtk Color Chooser Widget
box.add(labelC);
colorChooserC=new interface_ColorChooser (new Gtk.ColorChooserWidget ());
colorChooserC.connect();
box.add(colorChooserC.implementation);

window.show_all();
Gtk.main();