Schmaehgrunza
Schmaehgrunza

Reputation: 272

absolute fixed positioning in GTK.Fixed

For learning, I've written a toolframe class (gtk 3.24.20), the toolframes are moveable and expandable. All toolframes are appended to a Gtk.Fixed widget.
documentation about Gtk.Fixed "The GtkFixed widget is a container which can place child widgets at fixed positions and with fixed sizes, given in pixels. GtkFixed performs no automatic layout management"
If i use the instance methods of Gtk.Fixed put and move to put the toolframe to (100,100) , sometimes the gtk layout calculation put the toolframe back to the orginal position (0,0). for example if i'm resizing the window. or click on a button
if i use widget method size_allocate and set x,y, width and height, it also happens, suddenly the width changes and x,y goes back to 0,0

i had to watch the signal size-allocate and every time, if gtk layout changes my toolframe's x,y, width height dimensions, i have to overwrite the allocation to put it back to my position, width and height.

I wonder, because documentation says. on Gtk.Fixed no layout managment is performed. Do i really have to always overwrite the allocation, when the size-allocate signal is fired for my toolframe, or did i something wrong.

Another question- if i want to destroy a GOBJECT, do i have to disconnect all signal handlers before destroying, or can i simply destroy it, without disconnecting (about memory leaks)

class with an example:

#!/usr/bin/gjs
const Gtk=       imports.gi.Gtk,
      Gio=       imports.gi.Gio,
      Gdk=       imports.gi.Gdk,
      GdkPixbuf= imports.gi.GdkPixbuf;

Gtk.init(null);

const window=          new Gtk.Window(),
      fixed_container= new Gtk.Fixed();



/* ==================================================================================================================================
   CLASS ToolFrame (fixed_container, x, y, image_URL_orPATH_orICON, title, tooltip_text, content)
   ==================================================================================================================================
   gtk version: 3.24.20 - depends on Gtk, Gio, Gdk, GdkPixbuf
   --------------------
   creates a moveable toolframe with titlebar and content, the toolframe can be minimized only showing the titlebar or can be expanded
   most of the toolframe style can be changed over static object ToolFrame.standards (for explaination look at ToolFrame.standards)
   before creation. In standard the toolframe has four buttons, a minimize button, an expand button, a pin button, and a close button

   Gtk widget structure:
   ---------------------
   eventbox_for_vbox (root widget) -> vbox -> eventbox_for_hbox -> hbox (titlebar)-> image, label, buttons
                                           -> scrolled window   -> content

   parameters:
   -----------
   fixed_container          GOBJ       ... a Gtk.Fixed widget - parent container for all ToolFrame instances
   x                        NUMB (INT) ... x position of the ToolFrame instance relative to parent container origin
   y                        NUMB (INT) ... y position of the ToolFrame instance relative to parent container origin
   image_URL_orPATH_orICON  STR        ... can be an URL or a PATH or an ICON name, icon names have to start with "icon://",
                                           null or undefined, means no image
   title                    STR        ... the title of the ToolFrame instance
   tool_tip                 STR        ... tooltip text (hovering title) - null or undefined means no tooltip
   content                  GOBJ       ... a Gtk widget which is shown, when the ToolFrame instance is expanded

   for detailed explainations look inside Gnome JavaScript class comments!

   public instance properties:
   ---------------------------
   fixed_container, root_widget, widgets, style_contexts, isPinned, isExpanded

   public prototype properties:
   ----------------------------
   toolframes, toolframe_inAction

   public instance methods:
   ------------------------
   set_AsLastChild, move, get_expanded_size, set_expanded_size

   -----------------------------------------------------------------------------------------------------------------------------------*/
{ //-----> blockscope Class ToolFrame
 //-----------------------------------------------------------------------
 //----------------- CONSTRUCTOR

var ToolFrame=function ToolFrame (fixed_container, x, y, image_URL_orPATH_orICON, title, tooltip_text, content)
    {
    const widgets=new Object (), standards=ToolFrame.standards, style_contexts=new Object ();
    var width, height, cssProvider;

    widgets.eventbox_for_hbox= new Gtk.EventBox();                                                  //-- eventbox parent of hbox
    widgets.eventbox_for_vbox= new Gtk.EventBox();                                                  //-- eventbox parent of vbox -> ToolFrame instance root_widget!
    widgets.hbox=              new Gtk.Box({ spacing: standards.titlebar_spacing, valign:3 });      //-- titlebar = hbox
    widgets.vbox=              new Gtk.Box({ orientation:1, spacing: standards.toolframe_spacing}); //-- children hbox (titlebar) and scrolled_window (holds content)
    widgets.scrolled_window=   new Gtk.ScrolledWindow ({  vexpand:true });                          //-- content added to scrolled_window

    //-- references to toolframe instance used by signals functions (circular references)
    widgets.eventbox_for_vbox.toolframe=this;
    widgets.hbox.toolframe=this;

    //-- creating titlebar image and pixbuf, width and height defined in ToolFrame.standards
    if (image_URL_orPATH_orICON)
        {
        widgets.image=         new Gtk.Image({ halign:3, valign:3 });  //-- vertical center
        //-- ICON case
        if (image_URL_orPATH_orICON.indexOf("icon://")==0) widgets.image.set_from_icon_name (image_URL_orPATH_orICON.slice (7,image_URL_orPATH_orICON.length), standards.icon_size);
        //-- URL or PATH
        else
            {
            widgets.imagePixBuf=get_pixbuf_from_URLorPATH (image_URL_orPATH_orICON);
            if (widgets.imagePixBuf)
                {
                if (standards.image_width.constructor === Number)
                    {
                    width=standards.image_width;
                    if (standards.image_height =="auto") height=Math.round (width/widgets.imagePixBuf.width*widgets.imagePixBuf.height);
                    }
                if (standards.image_height.constructor === Number)
                    {
                    height=standards.image_height;
                    if (standards.image_width == "auto") width=Math.round (height/widgets.imagePixBuf.height*widgets.imagePixBuf.width);
                    }
                widgets.image.set_from_pixbuf (widgets.imagePixBuf.scale_simple (width, height, GdkPixbuf.InterpType.BILINEAR));
                }
            }
        widgets.hbox.pack_start(widgets.image,false,false,0);
        }

    //-- titlebar title
    widgets.label=             new Gtk.Label ({ label:title }); widgets.hbox.pack_start(widgets.label,false,false,0);
    cssProvider=new Gtk.CssProvider();
    cssProvider.load_from_data ("* "+standards.title_css_STR);
    widgets.label.get_style_context().add_provider (cssProvider,0);
    if (tooltip_text) widgets.label.set_tooltip_text (tooltip_text);

    //-- determine titlebar buttons width and height, defined in ToolFrame.standards
    if (standards.button_width.constructor === Number) width=standards.button_width; else width=-1;
    if (standards.button_height.constructor === Number) height=standards.button_height; else height=-1;

    //-- creating titlebar buttons according to ToolFrame.standards
    cssProvider=new Gtk.CssProvider();
    cssProvider.load_from_data ("* "+standards.buttons_css_STR);
    if (standards.buttons_creation.close)
        {
        widgets.button_close=  new Gtk.Button ({ label:standards.button_close_char, halign:3, valign:3 }),
        widgets.button_close.connect("clicked", button_close_clicked);
        widgets.hbox.pack_end(widgets.button_close,false,false,0);
        widgets.button_close.set_size_request (width, height);
        widgets.button_close.get_style_context().add_provider(cssProvider,0);
        if (standards.button_tooltip) widgets.button_close.set_tooltip_text("close");
        }
    if (standards.buttons_creation.pin)
        {
        if (standards.isPinned) widgets.button_pin=new Gtk.Button ({ label:standards.button_pinned_char, halign:3, valign:3 });
        else widgets.button_pin=new Gtk.Button ({label:standards.button_pin_char, halign:3, valign:3 });
        widgets.button_pin.connect("clicked", button_pin_clicked);
        widgets.hbox.pack_end(widgets.button_pin,false,false,0);
        widgets.button_pin.set_size_request (width, height);
        widgets.button_pin.get_style_context().add_provider(cssProvider,0);
        if (standards.button_tooltip) widgets.button_pin.set_tooltip_text("pin");
        }

    if (standards.buttons_creation.minimize_expand)
        {
        widgets.button_expand=    new Gtk.Button ({ label:standards.button_expand_char, halign:3, valign:3 }),
        widgets.button_expand.connect("clicked", button_expand_clicked);
        widgets.hbox.pack_end(widgets.button_expand,false,false,0);
        widgets.button_expand.set_size_request (width, height);
        widgets.button_expand.get_style_context().add_provider(cssProvider,0);
        if (standards.button_tooltip) widgets.button_expand.set_tooltip_text("expand");

        widgets.button_minimize=  new Gtk.Button ({label:standards.button_minimize_char, halign:3, valign:3 }),
        widgets.button_minimize.connect("clicked", button_minimize_clicked);
        widgets.hbox.pack_end(widgets.button_minimize,false,false,0);
        widgets.button_minimize.set_size_request (width, height);
        widgets.button_minimize.get_style_context().add_provider(cssProvider,0);
        if (standards.button_tooltip) widgets.button_minimize.set_tooltip_text("minimize");

        //-- sensitivity button_minimize, button_expand
        if (standards.content_visible_atStart) widgets.button_expand.set_sensitive(false);
        else widgets.button_minimize.set_sensitive(false);

        }

    /*   setting references for ToolFrame instance; fixed_container -> stage container for all ToolFrame instances
         root_widget -> representing ToolFrame instance on screen; widgets -> folder for all widgets including image pixbuffer;
         style_contexts -> holds the style context of vbox and hbox and some button chars */
    widgets.content=content;
    this.fixed_container=fixed_container;        //-- the parent of all ToolFrame instances, a Gtk.Fixed
    this.root_widget=widgets.eventbox_for_vbox;  //-- the ToolFrame instance root widget, an eventbox (for vbox)
    this.widgets=widgets;                        //-- an object, which holds references to all widgets inclusive imagePixBuf
    this.style_contexts=style_contexts;          //-- an object, which holds references to style contexts (hbox, vbox)

    //-- adding buttons_pin.. characters to style_contexts
    style_contexts.button_pin_char=standards.button_pin_char;
    style_contexts.button_pinned_char=standards.button_pinned_char;

    //-- css, style of hbox
    cssProvider=new Gtk.CssProvider();
    cssProvider.load_from_data(".divider "+standards.divider_css_borderSTR);
    style_contexts.hbox=widgets.hbox.get_style_context();
    if (standards.content_visible_atStart) style_contexts.hbox.add_class("divider");
    style_contexts.hbox.add_provider(cssProvider,0);
    if (standards.titlebar_spacing.constructor === Number) widgets.hbox.spacing=standards.titlebar_spacing;
    if (standards.titlebar_margin_start.constructor === Number) widgets.hbox.margin_start=standards.titlebar_margin_start;            //--set margin values
    if (standards.titlebar_margin_end.constructor === Number) widgets.hbox.margin_end=standards.titlebar_margin_end;
    if (standards.titlebar_margin_top.constructor === Number) widgets.hbox.top_right=standards.titlebar_margin_top;
    if (standards.titlebar_margin_bottom.constructor === Number) widgets.hbox.bottom_right=standards.titlebar_margin_bottom;

    //-- css, style of vbox
    cssProvider=new Gtk.CssProvider();
    cssProvider.load_from_data("* "+standards.toolframe_css_STR+" .moving "+standards.toolframe_move_css_STR);
    style_contexts.vbox=widgets.vbox.get_style_context ();
    style_contexts.vbox.add_provider (cssProvider,0);
    widgets.vbox.override_background_color (Gtk.StateFlags.NORMAL, standards.toolframe_backgroundColor);
    if (standards.toolframe_spacing.constructor === Number) widgets.vbox.spacing=standards.toolframe_spacing;

    //-- set content margin values
    if (standards.content_margin_start.constructor === Number) widgets.scrolled_window.margin_start=standards.content_margin_start;   //--set margin values
    if (standards.content_margin_end.constructor === Number) widgets.scrolled_window.margin_end=standards.content_margin_end;
    if (standards.content_margin_top.constructor === Number) widgets.scrolled_window.top_right=standards.content_margin_top;
    if (standards.content_margin_bottom.constructor === Number) widgets.scrolled_window.bottom_right=standards.content_margin_bottom;

    //-- PRIVATE folder on instance - holds x,y (actual position), expanded_width and expanded_height
    this[private_key]={
        _x:x, _y:y,
        _expanded_width:standards.toolframe_expanded_width,
        _expanded_height:standards.toolframe_expanded_height}

    //-- set toolframe minimum width and height
    if (standards.toolframe_minimum_width.constructor  === Number) width=standards.toolframe_minimum_width; else width=-1;
    if (standards.toolframe_minimum_height.constructor === Number) height=standards.toolframe_minimum_height; else height=-1;
    this.root_widget.set_size_request (width, height);

    //-- build widget tree
    widgets.eventbox_for_hbox.add (widgets.hbox);
    widgets.vbox.add (widgets.eventbox_for_hbox);
    widgets.scrolled_window.add (content);
    widgets.vbox.add (widgets.scrolled_window);
    widgets.eventbox_for_vbox.add (widgets.vbox);
    fixed_container.add (widgets.eventbox_for_vbox);

    //-- instance properties isPinned, isExpanded, push to toolframes prototype array collection
    this.isPinned=standards.isPinned;                            //-- indicates, that the toolframe is pinned
    if (standards.content_visible_atStart) this.isExpanded=true; //-- indicates, that the toolframe is expanded
    else this.isExpanded=false;
    ToolFrame.prototype.toolframes.push(this);

    //-- connect to hbox, vbox signals
    widgets.eventbox_for_hbox.connect("button-press-event",start_moving);
    widgets.eventbox_for_hbox.connect("button-release-event",stop_moving);
    widgets.eventbox_for_vbox.connect ("button-press-event",this.set_AsLastChild.bind(this));
    widgets.eventbox_for_vbox.connect ("size-allocate",observe_root_widget.bind(this));
    //-- in this handler the scrolled_window visibility is set to standards.content_visible_atStart at first show signal
    this[private_key].handler_first_show_signal=widgets.scrolled_window.connect("show", observe_first_show_signal);
    }
//----------------- CONSTRUCTOR -- END
//-----------------------------------------------------------------------
//------------------PROTOTYPE

ToolFrame.prototype.toolframes=[];            //-- a collection of all toolframes created
ToolFrame.prototype.toolframe_inAction=null;  //-- the toolframe, which is moved, when moving takes place

//-- put the ToolFrame instance as last child of the fixed_container, its like increasing zIndex, to bring it on top
ToolFrame.prototype.set_AsLastChild=function ()
    {
    const children=this.fixed_container.get_children(), l=children.length;

    if (this.root_widget !== children[l-1])
        {
        this.fixed_container.remove (this.root_widget);
        this.fixed_container.add (this.root_widget);
        }
    }

//-- moves the toolframe to x,y (INT) position
ToolFrame.prototype.move=function (x,y)
    {
    const allocation=this.root_widget.get_allocation (), PRIVATE=this[private_key];
    PRIVATE._x=allocation.x=x; PRIVATE._y=allocation.y=y;
    observing_size_allocation=false;
    this.root_widget.size_allocate (allocation);
    observing_size_allocation=true;
    }
/* sets the expanded_width and expanded_height, which are stored in a PRIVATE instance folder - width, height INT
   you can set minimum_width and minimum_height over set_size_request on root_widget */
ToolFrame.prototype.set_expanded_size=function (width,height)
    {
    const PRIVATE=this[private_key];
    PRIVATE._expanded_width=width; PRIVATE._expanded_height=height;
    if (this.isExpanded) observe_root_widget.call (this, this.widgets.eventbox_for_vbox);
    }

//-- gets the expanded_width and expanded_height stored in the PRIVATE instance folder
ToolFrame.prototype.get_expanded_size=function ()
    {
    const PRIVATE=this[private_key];
    return { expanded_width: PRIVATE._expanded_width, expanded_height: PRIVATE._expanded_height };
    }

//------------------PROTOTYPE -- END
//-----------------------------------------------------------------------

//------------------------------------------------------------------------------------------------------------------------------------
//----------------- PRIVATE objects, properties, methods

let private_key=Symbol (),               //-- PRIVATE instance folder key
    observing_size_allocation=true,      //-- indicates, that the "size-allocate" signal shall be observed for uncontrollable automatic gtk allocation changes on the root widget
    isMoving=false,                      //-- indicates, when moving shall happen inside window "motion-notify-event" signal
    move_allocation=null,                //-- holds the actual allocation during movement
    moveStart_allocation=null,           //-- saves the ToolFrame instance allocation (x,y,width,height) at move start
    moveStart_eventCoords=null,          //-- saves the pointer coordinates at move start -> difference in pointer coordinates equals difference in instance allocation (x,y)
    titlebar_margin=0, content_margin=0, //-- saves standards titlebar_margin and content_margin

    //-----------------------------------------------------------------------
    //------------------------------------------------------ Signal functions
 
    button_minimize_clicked=function (button)
        {
        const actual_toolframe=button.get_parent().toolframe, PRIVATE=actual_toolframe[private_key],
              widgets=actual_toolframe.widgets;
        widgets.scrolled_window.set_visible (false);
        widgets.button_minimize.set_sensitive (false);
        widgets.button_expand.set_sensitive (true);
        actual_toolframe.style_contexts.hbox.remove_class ("divider");
        actual_toolframe.isExpanded=false;
        actual_toolframe.set_AsLastChild ();
        },
    button_expand_clicked=function (button)
        {
        const actual_toolframe=button.get_parent().toolframe, PRIVATE=actual_toolframe[private_key],
              widgets=actual_toolframe.widgets;
        widgets.scrolled_window.set_visible (true);
        widgets.button_minimize.set_sensitive (true);
        widgets.button_expand.set_sensitive (false);
        actual_toolframe.style_contexts.hbox.add_class ("divider");
        actual_toolframe.isExpanded=true;
        actual_toolframe.set_AsLastChild ();
        },
 
    button_pin_clicked=function (button)
        {
        const actual_toolframe=button.get_parent().toolframe;
        actual_toolframe.isPinned=!actual_toolframe.isPinned;
        if (actual_toolframe.isPinned) actual_toolframe.widgets.button_pin.label=actual_toolframe.style_contexts.button_pinned_char; 
        else actual_toolframe.widgets.button_pin.label=actual_toolframe.style_contexts.button_pin_char;
        actual_toolframe.set_AsLastChild ();
        },
    button_close_clicked=function (button) { button.get_parent().toolframe.root_widget.set_visible (false); },

    //-- connected to eventbox_for_hbox signal "button-press-event" , initiate moving
    start_moving=function (eventbox_for_hbox, event)
        {
        const actual_toolframe=eventbox_for_hbox.get_children()[0].toolframe;
        var eventCoords;

        if (actual_toolframe.isPinned) return;
        eventCoords=event.get_root_coords();               //-- coords relative to eventbox_for_hbox origin (hbox)
        isMoving=true;
        move_allocation=actual_toolframe.widgets.eventbox_for_vbox.get_allocation ();
        moveStart_allocation=actual_toolframe.widgets.eventbox_for_vbox.get_allocation ();
        moveStart_eventCoords={
            x: Math.trunc(eventCoords[1]),
            y: Math.trunc(eventCoords[2])};
        actual_toolframe.style_contexts.vbox.add_class("moving");
        ToolFrame.prototype.toolframe_inAction=actual_toolframe;
        },

    //-- connected to eventbox_for_hbox signal "button-release-event", stops moving, set necessary values back to null
    stop_moving=function (eventbox_for_hbox)
        {
        const actual_toolframe=eventbox_for_hbox.get_children()[0].toolframe;
        isMoving=false;
        move_allocation=null;
        moveStart_allocation=null;
        moveStart_eventCoords=null;
        ToolFrame.prototype.toolframe_inAction=null;
        actual_toolframe.style_contexts.vbox.remove_class("moving");
        },

    //-- connected to window signal "motion-notify-event", moves the toolframe with pointer device
    move_toolframe=function (window, event)
        {
        var actual_toolframe, PRIVATE, eventCoords;
        if (isMoving)
            {
            actual_toolframe=ToolFrame.prototype.toolframe_inAction, PRIVATE=actual_toolframe[private_key];
            eventCoords=event.get_root_coords();
            PRIVATE._x=move_allocation.x=Math.trunc(eventCoords[1])-moveStart_eventCoords.x+moveStart_allocation.x;
            PRIVATE._y=move_allocation.y=Math.trunc(eventCoords[2])-moveStart_eventCoords.y+moveStart_allocation.y;
            observing_size_allocation=false;
            actual_toolframe.root_widget.size_allocate (move_allocation);
            observing_size_allocation=true;
            }
        },

    /*   connected to eventbox_for_vbox signal "size-allocate", overwrites the gtk layout coords, dimensions
         gtk layout replaces the tool frame and its dimensions according to its inner rules,
         thats not very helpful, if the tool frame should be placed at different position, or should show up with different width and height, than gtk layout calculates.
         so to force gtk to accept different dimensions, the gtk signal "size allocate" for the tool frame (eventbox_for_vbox) is observed */
    observe_root_widget=function (eventbox_for_vbox)
        {
        var gtk_allocation, replacing, PRIVATE;

        if (observing_size_allocation)
            {
            gtk_allocation=eventbox_for_vbox.get_allocation (), replacing=false, PRIVATE=eventbox_for_vbox.toolframe[private_key];
            if (gtk_allocation.x != PRIVATE._x)
                {
                replacing=true;
                gtk_allocation.x=PRIVATE._x;
                }
            if (gtk_allocation.y != PRIVATE._y)
                {
                replacing=true;
                gtk_allocation.y=PRIVATE._y;
                }
            if (eventbox_for_vbox.toolframe.isExpanded)
                {
                if (PRIVATE._expanded_width.constructor === Number && gtk_allocation.width!=PRIVATE._expanded_width) 
                    {
                    replacing=true;
                    gtk_allocation.width=PRIVATE._expanded_width;
                    }
                if (PRIVATE._expanded_height.constructor === Number && gtk_allocation.height!=PRIVATE._expanded_height)
                    {
                    replacing=true;
                    gtk_allocation.height=PRIVATE._expanded_height;
                    }
                }

            if (replacing)
                {
                observing_size_allocation=false;                   //-- prohibit calls of size_allocate to infinity, because next line is causing another size-allocate event, which is immediately worked through
                this.root_widget.size_allocate (gtk_allocation);   //-- causes again a size-allocate event
                this.root_widget.set_clip (gtk_allocation);
                observing_size_allocation=true;                    //-- additionally size-allocate event is now worked through, allow observing again
                }
            }
        },

    /*   connected to scrolled_window (holds content) signal "show"; is needed to set the visibility according to standards content_visible_atStart property at first show signal
         after executing one time the handler is disconnected */
    observe_first_show_signal=function (scrolled_window)
        {
        var PRIVATE=scrolled_window.get_parent().get_parent().toolframe[private_key];
        scrolled_window.set_visible(ToolFrame.standards.content_visible_atStart);
        scrolled_window.disconnect (PRIVATE.handler_first_show_signal);
        PRIVATE.handler_first_show_signal=null
        },
    //------------------------------------------------------ Signal functions -- END
    //-----------------------------------------------------------------------

    //-- returns the image pixelbuffer from URL or PATH
    get_pixbuf_from_URLorPATH=function (URLorPATH)
        {
        var file, pixbuf=null;
        //-- URL contains //
        if (URLorPATH.indexOf("//")>-1)
            {
            file=Gio.File.new_for_uri (URLorPATH);
            try { pixbuf=GdkPixbuf.Pixbuf.new_from_stream (file.read (null), null); }
            catch (error) { log (error+" - "+URLorPATH) }
            return pixbuf;
            }
        else return GdkPixbuf.new_from_file (URLorPATH);
        };

//-- connect to window signals
window.connect ("motion-notify-event", move_toolframe);

//----------------- PRIVATE objects, properties, methods
//------------------------------------------------------------------------------------------------------------------------------------
//----------------- STATIC object ToolFrame standards

ToolFrame.standards={
    isPinned: false,                        //-- indicates, if the tool frame can move
    toolframe_minimum_width: "auto",        //-- minimum width of tool frame in minimize appearance
    toolframe_minimum_height: "auto",       //-- minimum height of tool frame in minimize apperance
    toolframe_expanded_width: "auto",       //-- width of tool frame in expand appearance
    toolframe_expanded_height: "auto",           //-- height of tool frame in expand appearance
    image_width: "auto",                    //-- image minimum width and height
    image_height: 24,
    icon_size: Gtk.IconSize.LARGE_titlebar, //-- Look at Gtk.IconSize (24px)
    button_width: "auto",                   //-- button minimum width and height
    button_height: "auto",
    toolframe_spacing: 2, titlebar_spacing: 2,                                                         //-- vbox, hbox spacing
    titlebar_margin_start: 2, titlebar_margin_end:2, titlebar_margin_top:2, titlebar_margin_bottom:2,  //-- hbox margin values
    content_margin_start: 2, content_margin_end:2, content_margin_top:2, content_margin_bottom:2,      //-- content margin values
    content_visible_atStart: false,                                                                    //-- indicates, if content shall be visible at start
    buttons_creation: { minimize_expand:true, pin:true, close:true },                                  //-- indicates, which buttons shall be created
    button_minimize_char: "\u25B4",            //-- button characters
    button_expand_char: "\u25BE",
    button_pin_char: "\u21C4",
    button_pinned_char: "\u21B9",
    button_close_char: "X",
    button_tooltip: true,                      //-- indicates, if tooltips shall be shown by hovering buttons

    //-- css style strings
    toolframe_css_STR: "{ border:1px solid black }",
    toolframe_move_css_STR: "{ border-style:dashed }",
    toolframe_backgroundColor: new Gdk.RGBA ({ red:1, green:1, blue:1, alpha:1}),                               //-- toolframe background color
    title_css_STR: "{ font-weight:bold }",
    buttons_css_STR: "{}",
    divider_css_borderSTR: "{ border-bottom-width:1px; border-bottom-style:solid; border-bottom-color:black }", //-- divider between titlebar and content -> hbox border bottom
    }

//-- standards titlebar_margin, content_margin
Object.defineProperties (ToolFrame.standards,{
    titlebar_margin : { get: () => titlebar_margin, set: function (value) {
        titlebar_margin=value;
        this.titlebar_margin_start=value; this.titlebar_margin_end=value;
        this.titlebar_margin_top=value;this.titlebar_margin_bottom=value;}, enumerable:true, configurable:false },
    content_margin : { get: () => content_margin, set: function (value) {
        content_margin=value;
        this.content_margin_start=value; this.content_margin_end=value;
        this.content_margin_top=value;this.content_margin_bottom=value;}, enumerable:true, configurable:false }});

//----------------- STATIC object ToolFrame standards -- END
//------------------------------------------------------------------------------------------------------------------------------------

} //-----> blockscope Class ToolFrame

/* ==================================================================================================================================
   CLASS ToolFrame -- END
   ================================================================================================================================== */

let file=Gio.File.new_for_uri("https://i.ytimg.com/vi/fa5e_r6ZPoM/maxresdefault.jpg"),
    image=Gtk.Image.new_from_pixbuf (GdkPixbuf.Pixbuf.new_from_stream(file.read(null),null));

new ToolFrame(fixed_container,20, 20, "http://www.iconeasy.com/icon/png/File%20Type/Software%20Files/Txt.png", "Text Viewer", "view text", new Gtk.Label ({label:"hello World\n Hello World Hello World"}));

ToolFrame.standards.toolframe_expanded_width=500;
ToolFrame.standards.toolframe_expanded_height=300;
ToolFrame.standards.buttons_width=24;
new ToolFrame(fixed_container,20, 60, "http://icons.iconseeker.com/png/fullsize/glaze/folder-image.png", "Image Viewer", "view image", image);

window.add(fixed_container);

window.set_title("Toolframe Example");
window.connect('destroy', () => { Gtk.main_quit(); });
window.set_size_request (740, 600);
window.show_all(); Gtk.main();

Upvotes: 0

Views: 685

Answers (1)

Schmaehgrunza
Schmaehgrunza

Reputation: 272

it was my fault.
To bring a widget on top of drawing action (zIndex) everytime a click happened inside the widget, i removed it from the Fixed container and add it at last child, this caused the strange action.

I now use Gtk.Overlay for the zIndex and every toolframe has its own Gtk.Fixed container as stage and is added to Gtk.Overlay.

I have a simplier example with Images. class MovingImages.
But how do you change the container(dashed box around the image) to a defined width and height smaller than the image?

#!/usr/bin/gjs
const Gtk=       imports.gi.Gtk,
      Gio=       imports.gi.Gio,
      GdkPixbuf= imports.gi.GdkPixbuf;

Gtk.init(null);

const window=  new Gtk.Window(),
      box=     new Gtk.Box (),
      overlay= new Gtk.Overlay();

box.add (new Gtk.Label ({ label:"Drag The Images!", hexpand:true, halign:3, valign:3 }))

//-- class MovingImage
{
var MovingImage=function (overlay, x, y, image_url)
    {
    let file=Gio.File.new_for_uri (image_url),
        image=Gtk.Image.new_from_pixbuf (GdkPixbuf.Pixbuf.new_from_stream(file.read(null),null)),
        cssProvider=new Gtk.CssProvider(), allocation;

    this.overlay=overlay;
    this.stage=new Gtk.Fixed ();
    this.container=new Gtk.EventBox ();
    this.image=image;
    this.container.movingImage=this; //-- circular reference

    cssProvider.load_from_data("* {border:1px dashed black;}");
    this.container.get_style_context().add_provider (cssProvider,0);

    this.container.add (image);
    this.stage.put (this.container,x,y);

    overlay.add_overlay (this.stage);
    overlay.set_overlay_pass_through(this.stage,true);

    this.container.connect ("button-press-event", container_button_down);
    this.container.connect ("button-release-event", container_button_up);
    }

MovingImage.prototype.instance_inAction=null;

let isMoving=false,
    moveStart_allocation=null,
    moveStart_event_coords=null,

    container_button_down=function (container, event)
        {
        const event_coords=event.get_root_coords();
        isMoving=true;
        MovingImage.prototype.instance_inAction=container.movingImage;
        moveStart_allocation=container.get_allocation ();
        moveStart_event_coords={
            x: Math.trunc(event_coords[1]),
            y: Math.trunc(event_coords[2])};
        container.movingImage.overlay.reorder_overlay (container.movingImage.stage,-1);

        },
    container_button_up=function ()
        {
        isMoving=false;
        MovingImage.prototype.instance_inAction=null;
        moveStart_allocation=null;
        moveStart_event_coords=null;
        },

    container_move=function (window, event)
        {
        var event_coords, movingImage;
        if (isMoving)
            {
            event_coords=event.get_root_coords();
            movingImage=MovingImage.prototype.instance_inAction;
            movingImage.stage.move (movingImage.container,
                Math.trunc(event_coords[1]-moveStart_event_coords.x+moveStart_allocation.x),
                Math.trunc(event_coords[2]-moveStart_event_coords.y+moveStart_allocation.y));
            }
        };

window.connect ("motion-notify-event", container_move);
}
//-- class MovingImage -- END
overlay.add (box);
window.add (overlay);

let image1=new MovingImage (overlay,20,20,"https://icons.iconarchive.com/icons/shrikant-rawa/animals/128/dog-icon.png"),
    image2=new MovingImage (overlay,100,60,"https://icons.iconarchive.com/icons/martin-berube/square-animal/128/Bull-icon.png");

window.set_title("MovingImage Example");
window.connect('destroy', () => { Gtk.main_quit(); });
window.set_size_request (640, 480);
window.show_all(); Gtk.main();

to change the width and height, i have to connect to the "size-allocate" signal of the container and always overwrite the allocation this.container.connect ("size-allocate", reallocate_container);

    reallocate_container=function (container)
        {
        const allocation=container.get_allocation ();
        allocation.width=50;
        allocation.height=50;
        container.set_allocation(allocation);
        container.set_clip (allocation);
        }

isnt there a way to set the width and height without this? the complete change example:

#!/usr/bin/gjs
const Gtk=       imports.gi.Gtk,
      Gio=       imports.gi.Gio,
      GdkPixbuf= imports.gi.GdkPixbuf;

Gtk.init(null);

const window=  new Gtk.Window(),
      box=     new Gtk.Box (),
      overlay= new Gtk.Overlay();

box.add (new Gtk.Label ({ label:"Drag The Images!", hexpand:true, halign:3, valign:3 }))

//-- class MovingImage
{
var MovingImage=function (overlay, x, y, image_url)
    {
    let file=Gio.File.new_for_uri (image_url),
        image=Gtk.Image.new_from_pixbuf (GdkPixbuf.Pixbuf.new_from_stream(file.read(null),null)),
        cssProvider=new Gtk.CssProvider(), allocation;

    this.overlay=overlay;
    this.stage=new Gtk.Fixed ();
    this.container=new Gtk.EventBox ();
    this.image=image;
    this.container.movingImage=this; //-- circular reference

    cssProvider.load_from_data("* {border:1px dashed black;}");
    this.container.get_style_context().add_provider (cssProvider,0);

    this.container.add (image);
    this.stage.put (this.container,x,y);

    overlay.add_overlay (this.stage);
    overlay.set_overlay_pass_through(this.stage,true);

    this.container.connect ("size-allocate", reallocate_container);
    this.container.connect ("button-press-event", container_button_down);
    this.container.connect ("button-release-event", container_button_up);
    }

MovingImage.prototype.instance_inAction=null;

let isMoving=false,
    moveStart_allocation=null,
    moveStart_event_coords=null,

    container_button_down=function (container, event)
        {
        const event_coords=event.get_root_coords();
        isMoving=true;
        MovingImage.prototype.instance_inAction=container.movingImage;
        moveStart_allocation=container.get_allocation ();
        moveStart_event_coords={
            x: Math.trunc(event_coords[1]),
            y: Math.trunc(event_coords[2])};
        container.movingImage.overlay.reorder_overlay (container.movingImage.stage,-1);
        },
    container_button_up=function ()
        {
        isMoving=false;
        MovingImage.prototype.instance_inAction=null;
        moveStart_allocation=null;
        moveStart_event_coords=null;
        },

    container_move=function (window, event)
        {
        var event_coords, movingImage;
        if (isMoving)
            {
            event_coords=event.get_root_coords();
            movingImage=MovingImage.prototype.instance_inAction;
            movingImage.stage.move (movingImage.container,
                Math.trunc(event_coords[1]-moveStart_event_coords.x+moveStart_allocation.x),
                Math.trunc(event_coords[2]-moveStart_event_coords.y+moveStart_allocation.y));
            }
        },

    reallocate_container=function (container)
        {
        const allocation=container.get_allocation ();
        allocation.width=50;
        allocation.height=50;
        container.set_allocation(allocation);
        container.set_clip (allocation);
        }

window.connect ("motion-notify-event", container_move);
}
//-- class MovingImage -- END
overlay.add (box);
window.add (overlay);

let image1=new MovingImage (overlay,20,20,"https://icons.iconarchive.com/icons/shrikant-rawa/animals/128/dog-icon.png"),
    image2=new MovingImage (overlay,100,60,"https://icons.iconarchive.com/icons/martin-berube/square-animal/128/Bull-icon.png");

window.set_title("MovingImage Example");
window.connect('destroy', () => { Gtk.main_quit(); });
window.set_size_request (640, 480);
window.show_all(); Gtk.main();

Upvotes: 0

Related Questions