Buttons coordinates

33 views
Skip to first unread message

Damiano Rocchi

unread,
Nov 5, 2022, 6:32:42 PM11/5/22
to fltk.general
Hi, my project is a image visualizer, where the image are greater the screen, with some button over the image and the scroll area is big how the image. There are 2 buttons for zoom + or zoom -. I write the callback for zoom -, and when I zoom -, the button over the image are reposition relative to the screen area that I see in that moment, but I desire to position the buttons relative to the whole image.
Can you help me, please ?

Matthias Melcher

unread,
Nov 6, 2022, 10:03:24 AM11/6/22
to fltk.general
Fl_Scroll will reposition all widgets whenever you are scrolling your content. You could either put your buttons outside of the scroll group, which is generally no a bad idea. FLTK does not guarantee that widgets are drawn in a specific order, so you buttons may end up under the image and be invisible.

If all you need to scroll around is the background, but none of the button on top, instead of using Fl_Scroll, you could use aFl_Group, adding a horizontal and vertical Fl_Scrollbar next to it which move the background around inside the group while leaving all other widgets alone.

Another trick would be to replace the callbacks in you Fl_Scroll for Fl_Scrollbar scrollbar and Fl_Scrollbar hscrollbar to call the original callback, and the reposition your widgets back to their original positions.

Ian MacArthur

unread,
Nov 6, 2022, 11:20:35 AM11/6/22
to fltk.general
Might be possible to "float" the buttons over the scroll too, though that would really need a derived widget to make sure the drawing was done in the right order I suppose.


 

Damiano Rocchi

unread,
Nov 6, 2022, 1:03:22 PM11/6/22
to fltk.general
My problem start when I zoom the image, because the position of button inside are changed. I try to reposition the button, but the coordinates and the result change based on the part of image I see. For exaple if i see the upper part of image, the button position will be in the upper part of image. Same thing with lower.
I post the code for fltk 1.4


#include <stdlib.h>
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_SVG_Image.H>
#include <FL/fl_message.H>
#include <Fl/Fl_Scroll.H>
#include <Fl/Fl_Multiline_Output.H>
#include <Fl/Fl_PNG_Image.H>
#include <FL/fl_ask.H>
#include <FL/Fl_Tabs.H>
#include <FL/Fl_Group.H>
#include <FL/Fl_Button.H>

Fl_SVG_Image *imm_villaggio = new Fl_SVG_Image("villaggio.svg");
double i = 0, l = 0;
int a = 20, b = 2000, c = 5, d = 2, z = 0, x = 0;
char buffer[200];

class Imm : public Fl_Box{

    protected:
               
                int handle( int v )
                {

                    int ret = Fl_Box::handle(v);
                    if( v == FL_PUSH )
                    {
                     
                        fl_alert("Popolazione 3000\nCiviltà 5\nBrutalità 5\nComportamento 0\nCommercio 2\n ");    
                        return(0);

                    }
                    else if( v == FL_RELEASE )
                        return(0);
                       

                    return(0);
                }

    public:

                Imm( int X, int Y, int W, int H, const char *L = 0) : Fl_Box(X, Y, W, H, L){

                    image(imm_villaggio);
                    box(FL_NO_BOX);

                }      

};

void p_zoom();
void m_zoom();
                       
Fl_SVG_Image *svg = new Fl_SVG_Image("Mappa.svg");
Fl_Box *box;
Imm *ins, *insd;
Fl_Scroll *scroll;
Fl_Window *win;
Fl_Tabs *tab;
Fl_Button *più;
Fl_Button *meno;

int main(int argc, char **argv)
{

    win = new Fl_Window(0, 0, 1440, 880, "Progetto");
    scroll = new Fl_Scroll(0, 120, 1456, 768);

    win->begin();

    tab = new Fl_Tabs(0, 0, 1440, 120);

    Fl_Group generale(20, 30, 1440, 120, "Generale");

    Fl_Button fine_turno(30, 40, 100, 40, "Fine turno");
    più = new Fl_Button(200, 50, 30, 20, "+");
    più->callback( (Fl_Callback *)p_zoom);
    meno = new Fl_Button(200, 80, 30, 20, "-");
    meno->callback( (Fl_Callback *)m_zoom);

    generale.end();

    Fl_Group reclutamento(20, 30, 1440, 120, "Reclutamento");


    reclutamento.end();

    Fl_Group personale(20, 30, 1440, 120, "Personale");


    personale.end();
   
    Fl_Group missione(20, 30, 1440, 120, "Missione");

   

    missione.end();

    Fl_Group diplomazia(20, 30, 1440, 120, "Diplomazia");

    diplomazia.end();


    tab->end();

    tab->clear_visible_focus();




    scroll->begin();

    box = new Fl_Box(120, 60, 3500, 3500 );
    ins = new Imm(1127, 2860, 50, 50);
    //insd = new Imm(0, 3500, 50, 50);
   
    scroll->end();

    win->end();
 

    box->image(svg);
    box->set_visible_focus();
    win->resizable(box);
    sprintf(buffer, "Giocatore\n\nZona %d\nAbitanti %d\nCiviltà %d\nBrutalità %d\nCommercio %d", a, b, c, c, d);
 
    win->show();
    return(Fl::run());

}

/* zoom + ( not tested ) */
void p_zoom()
{

    i = box->h();
    i = i / 100;
    i = i * 120;
    box->size( i, i );
    svg->resize(i, i);
    l = i;
    i = (1127 * i)/3500;
    l = (2860 * l)/3500;
    l = l * 81.714285714;
    Imm ins(i, l, 50, 50);
    box->image(svg);
    box->redraw();
    i = 0;
    l = 0;
    più->clear_visible_focus();
    box->set_visible_focus();

}

/* zoom - */
void m_zoom()
{

    i = box->h();
    i = i / 100;
    i = i * 80;
    box->size( i, i );
    svg->resize(i, i);
    i = ins->x();
    l = ins->y();
    i = i / 100;
    i = i * 80;
    l = l / -6.8;
    z = i;
    x = l;

    box->image(svg);
    box->redraw();
   
    scroll->begin();

    ins->position(z, x);
    //insd->position(900, 0);
    ins->redraw();
    insd->redraw();
    scroll->redraw();
    scroll->end();    
    i = 0;
    l = 0;
    meno->clear_visible_focus();
    box->set_visible_focus();

}

Damiano Rocchi

unread,
Nov 8, 2022, 8:29:49 AM11/8/22
to fltk.general
What is the right order ?
Scroll, box, image and buttons ?

Il giorno domenica 6 novembre 2022 alle 17:20:35 UTC+1 Ian MacArthur ha scritto:

imm

unread,
Nov 8, 2022, 11:01:20 AM11/8/22
to fltkg...@googlegroups.com
On Sun, 6 Nov 2022 at 18:03, Damiano Rocchi wrote:
>
> My problem start when I zoom the image, because the position of button inside are changed. I try to reposition the button, but the coordinates and the result change based on the part of image I see. For exaple if i see the upper part of image, the button position will be in the upper part of image. Same thing with lower.

I'm sorry, but I do not understand what effect you are hoping to achieve here.
Given the size of your map image tile, I'd be inclined to render it
once, zoomed large, to an offscreen surface, and then just extract the
specific region I wanted and zoom that down to the required
resolution.
The actual boxes within the scroll would not then have to change size
at all, making the rest of your calculations much simpler.

But, as I said, I am not sure what effect you hope to achieve...

> I post the code for fltk 1.4
>

There are some issues with your code - I will point them out here, in
case any of them are contributing to your difficulties.


>
> #include <stdlib.h>
> #include <FL/Fl.H>
> #include <FL/Fl_Window.H>
> #include <FL/Fl_Box.H>
> #include <FL/Fl_SVG_Image.H>
> #include <FL/fl_message.H>
> #include <Fl/Fl_Scroll.H>

I assume you are on WIndows?
Many of these say "Fl" but they ought to say "FL" - Windows is less
case sensitive so you can get away with that, but it is still wrong...

> #include <Fl/Fl_Multiline_Output.H>
> #include <Fl/Fl_PNG_Image.H>
> #include <FL/fl_ask.H>
> #include <FL/Fl_Tabs.H>
> #include <FL/Fl_Group.H>
> #include <FL/Fl_Button.H>
>
> Fl_SVG_Image *imm_villaggio = new Fl_SVG_Image("villaggio.svg");

Assigning the image types in the declaration is not wrong as such, but
it will affect the order of initialisation, since this will happen
before main() is called, in effect - it is probably better to assign
the images to the image pointers in main rather than in the
declaration.

> double i = 0, l = 0;

By convention, I think most (old!) programmers expect "i" and "l" to
name integer types....!

> int a = 20, b = 2000, c = 5, d = 2, z = 0, x = 0;
> char buffer[200];
>
> class Imm : public Fl_Box{
>
> protected:
>
> int handle( int v )
> {
>
> int ret = Fl_Box::handle(v);
> if( v == FL_PUSH )
> {
>
> fl_alert("Popolazione 3000\nCiviltà 5\nBrutalità 5\nComportamento 0\nCommercio 2\n ");
> return(0);

This should return 1, not 0

>
> }
> else if( v == FL_RELEASE )
> return(0);

This should return 1, not 0

>
>
> return(0);

This should return "ret", not 0

> }
>
> public:
>
> Imm( int X, int Y, int W, int H, const char *L = 0) : Fl_Box(X, Y, W, H, L){
>
> image(imm_villaggio);
> box(FL_NO_BOX);

It seems extremely unlikely this is the correct box type. You probably
want FL_FLAT_BOX here. The NO_BOX type will not be redrawn after
damage, since there *is* no box to draw; that will not be what you
want. The NO_BOX type is for very special cases that I do not think
you want.

>
> }
>
> };
>
> void p_zoom();
> void m_zoom();
>
> Fl_SVG_Image *svg = new Fl_SVG_Image("Mappa.svg");

See comment above about initialisation...

> Fl_Box *box;
> Imm *ins, *insd;
> Fl_Scroll *scroll;
> Fl_Window *win;
> Fl_Tabs *tab;
> Fl_Button *più;

Using accented characters in variable names *still* causes portability
issues, so be aware his might choke on some systems.


> Fl_Button *meno;
>
> int main(int argc, char **argv)
> {
>
> win = new Fl_Window(0, 0, 1440, 880, "Progetto");
> scroll = new Fl_Scroll(0, 120, 1456, 768);

These sizes look wrong - the scroll is larger than the window that
contains it. This is not allowed.

>
> win->begin();
>
> tab = new Fl_Tabs(0, 0, 1440, 120);
>
> Fl_Group generale(20, 30, 1440, 120, "Generale");

This group is too large for the tab that contains it (all the groups
look to be too big for the tab, in fact)

>
> Fl_Button fine_turno(30, 40, 100, 40, "Fine turno");
> più = new Fl_Button(200, 50, 30, 20, "+");
> più->callback( (Fl_Callback *)p_zoom);
> meno = new Fl_Button(200, 80, 30, 20, "-");
> meno->callback( (Fl_Callback *)m_zoom);
>
> generale.end();
>
> Fl_Group reclutamento(20, 30, 1440, 120, "Reclutamento");
>
>
> reclutamento.end();
>
> Fl_Group personale(20, 30, 1440, 120, "Personale");
>
>
> personale.end();
>
> Fl_Group missione(20, 30, 1440, 120, "Missione");
>
>
>
> missione.end();
>
> Fl_Group diplomazia(20, 30, 1440, 120, "Diplomazia");
>
> diplomazia.end();
>
>
> tab->end();
>
> tab->clear_visible_focus();

I do not think that clear_visible_focus() does what you think that it does.
What *do* you think that setting / clearing the visible focus is
doing? You do it a lot in the callbacks, but...

>
>
>
>
> scroll->begin();
>
> box = new Fl_Box(120, 60, 3500, 3500 );
> ins = new Imm(1127, 2860, 50, 50);

It seems unlikely these are the correct coordinates - these two
widgets will overlap, which will not behave as intended.

> //insd = new Imm(0, 3500, 50, 50);
>
> scroll->end();
>
> win->end();
>
>
> box->image(svg);
> box->set_visible_focus();
> win->resizable(box);
> sprintf(buffer, "Giocatore\n\nZona %d\nAbitanti %d\nCiviltà %d\nBrutalità %d\nCommercio %d", a, b, c, c, d);
>
> win->show();
> return(Fl::run());
>
> }
>
> /* zoom + ( not tested ) */
> void p_zoom()
> {
>
> i = box->h();
> i = i / 100;
> i = i * 120;
> box->size( i, i );
> svg->resize(i, i);
> l = i;
> i = (1127 * i)/3500;
> l = (2860 * l)/3500;
> l = l * 81.714285714;
> Imm ins(i, l, 50, 50);

This looks like an error: This is an extra declaration of "ins" that
will shadow the original global declaration above, this is certainly
not what you want. It also means that the "ins" seen in the "m_zoom"
callback is a different widget, since it will still be seeing the
original global version of "ins" not this shadow declaration.


> box->image(svg);
> box->redraw();
> i = 0;
> l = 0;
> più->clear_visible_focus();
> box->set_visible_focus();
>
> }
>
> /* zoom - */
> void m_zoom()
> {
>
> i = box->h();
> i = i / 100;
> i = i * 80;
> box->size( i, i );
> svg->resize(i, i);
> i = ins->x();
> l = ins->y();
> i = i / 100;
> i = i * 80;
> l = l / -6.8;
> z = i;
> x = l;
>
> box->image(svg);
> box->redraw();
>
> scroll->begin();
>
> ins->position(z, x);

At this point the global instance of "ins" may not be allocated the
way you expect, due to the shadow declaration in "p_zoom" above.

imm

unread,
Nov 8, 2022, 11:46:43 AM11/8/22
to fltkg...@googlegroups.com
Damiano, maybe this code (below) does what you were intending? I took
your example and adjusted it to compile for me, this is maybe the
effect you are trying to attain?

fltk-config --use-images --compile test.cxx


#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_SVG_Image.H>
#include <FL/Fl_Scroll.H>
#include <FL/fl_ask.H>
#include <FL/Fl_Tabs.H>
#include <FL/Fl_Group.H>
#include <FL/Fl_Button.H>

Fl_SVG_Image *imm_villaggio = NULL;
double i = 0, l = 0;
int a = 20, b = 2000, c = 5, d = 2, z = 0, x = 0;
char buffer[200];

class Imm : public Fl_Box{
protected:
int handle( int v )
{
int ret = Fl_Box::handle(v);
if( v == FL_PUSH )
{
fl_alert("Popolazione 3000\nCiviltà
5\nBrutalità 5\nComportamento 0\nCommercio 2\n ");
return 1;
}
else if( v == FL_RELEASE )
{
return 1;
}
return ret;
}
public:
Imm( int X, int Y, int W, int H, const char *L = 0) :
Fl_Box(X, Y, W, H, L){
image(imm_villaggio);
box(FL_FLAT_BOX);
}
};

void p_zoom();
void m_zoom();

Fl_SVG_Image *svg = NULL;
Fl_Box *box;
Imm *ins, *insd;
Fl_Scroll *scroll;
Fl_Window *win;
Fl_Tabs *tab;
Fl_Button *pxxx;
Fl_Button *meno;

int main(int argc, char **argv)
{
svg = new Fl_SVG_Image("tiger.svg");
imm_villaggio = new Fl_SVG_Image("penrose-tiling.svg");
imm_villaggio->resize (50,50);

win = new Fl_Window(0, 0, 1024, 800, "Progetto");
scroll = new Fl_Scroll(0, 120, 1024, 600);

win->begin();
tab = new Fl_Tabs(0, 0, 1024, 120);
Fl_Group generale(20, 30, 1024, 90, "Generale");
Fl_Button fine_turno(30, 40, 100, 40, "Fine turno");
pxxx = new Fl_Button(200, 50, 30, 20, "+");
pxxx->callback( (Fl_Callback *)p_zoom);
meno = new Fl_Button(200, 80, 30, 20, "-");
meno->callback( (Fl_Callback *)m_zoom);
generale.end();

Fl_Group reclutamento(20, 30, 1024, 90, "Reclutamento");
reclutamento.end();

Fl_Group personale(20, 30, 1024, 90, "Personale");
personale.end();

Fl_Group missione(20, 30, 1024, 90, "Missione");
missione.end();

Fl_Group diplomazia(20, 30, 1024, 90, "Diplomazia");
diplomazia.end();

tab->end();
tab->clear_visible_focus();

scroll->begin();
box = new Fl_Box(0, 0, 1024, 1024 );
svg->resize(1024, 1024 );
ins = new Imm(1024, 1024, 50, 50);

scroll->end();
scroll->box(FL_FLAT_BOX);

win->end();

box->image(svg);
win->resizable(box);
sprintf(buffer, "Giocatore\n\nZona %d\nAbitanti %d\nCiviltà
%d\nBrutalità %d\nCommercio %d", a, b, c, c, d);

win->show();
return(Fl::run());
} // main

/* zoom + ( not tested ) */
void p_zoom()
{
i = box->h();
i = i / 100;
i = i * 120;
box->size( i, i );
svg->resize(i, i);

int xx = scroll->xposition();
int yy = scroll->yposition();
ins->position(i - xx, i - yy);
box->image(svg);
scroll->redraw();
}

/* zoom - */
void m_zoom()
{
i = box->h();
i = i / 100;
i = i * 80;
box->size( i, i );
svg->resize(i, i);

int xx = scroll->xposition();
int yy = scroll->yposition();
ins->position(i - xx, i - yy);
box->image(svg);
scroll->redraw();
}
// end of file //

Damiano Rocchi

unread,
Nov 9, 2022, 2:46:45 PM11/9/22
to fltk.general
Thanks for help =)
This don't is what I intended. I would like to see after the zoom the ins position in the same position before the zoom.
The ins button can be in a random position of svg image. I proved to reduce the coordinates for 20%, but not work.
If I change the scrollvalue before zoom -, the ins position don't is the value that I have wrote in the code.

Ian MacArthur

unread,
Nov 10, 2022, 6:59:22 AM11/10/22
to fltk.general
On Wednesday, 9 November 2022 at 19:46:45 UTC Damiano Rocchi wrote:
Thanks for help =)
This don't is what I intended. I would like to see after the zoom the ins position in the same position before the zoom.
The ins button can be in a random position of svg image. I proved to reduce the coordinates for 20%, but not work.
If I change the scrollvalue before zoom -, the ins position don't is the value that I have wrote in the code.

Ah, OK, I think I begin to see what you want to do.

I think that you want to draw a large box - the map - and then position one (or presumably more) "villages" on top of that map. When the map is zoomed in or out, the villages should appear at the same position on the map, so need to be moved to account for the zoom...

The issue you have is that you need to account for both the scroll origin (which is non-zero) and for the scrollbar offsets, which will change as the scroll is moved, and with zoom factor...
I'll attach a reworked version of your code that attempts to account for these offsets, perhaps this will get you going?

That said, I do not think that overlaying multiple widgets on top of each other within a scroll region is necessarily going to be robust - I suspect there may be difficulties with that. 
Also as the map zooms in and out I suspect you will find that small positional errors accumulate over time and the villages will "drift" away form their initial positions (this will arise because of converting to and from integer and float positions with the code as presented here.)

I suspect it would be better to have some idealised representation of where each village is on the map, in some "ideal" coordinate system that never changes, and then use that to compute the "current" screen position of each village, accounting for the zoom factor and the current scroll position and offsets.


test_map.zip

Ian MacArthur

unread,
Nov 10, 2022, 9:01:44 AM11/10/22
to fltk.general
On Thursday, 10 November 2022 at 11:59:22 UTC Ian MacArthur wrote:

That said, I do not think that overlaying multiple widgets on top of each other within a scroll region is necessarily going to be robust - I suspect there may be difficulties with that. 
Also as the map zooms in and out I suspect you will find that small positional errors accumulate over time and the villages will "drift" away form their initial positions (this will arise because of converting to and from integer and float positions with the code as presented here.)

I suspect it would be better to have some idealised representation of where each village is on the map, in some "ideal" coordinate system that never changes, and then use that to compute the "current" screen position of each village, accounting for the zoom factor and the current scroll position and offsets.

I was sufficiently annoyed by the coordinate system in that last example that I cleaned it up and did a slightly more solid implementation.
FWIW, I still think this is the wrong way to achieve the result, but it seems to work "well enough" so may be satisfactory.

See attached...
To use this you need 2 SVG files, one called "tiger.svg" and one called "penrose-tiling.svg" for the demo to load at runtime.
Compile as:   fltk-config --use-images --compile test2.cxx

 
map_test.zip

Damiano Rocchi

unread,
Nov 10, 2022, 4:15:11 PM11/10/22
to fltk.general
Thank you for both code, especially the last work very well !!
You use a calculation for the position of the village that I need to study well the code for understand completely.
You result a great problem for me, thanks =D
Reply all
Reply to author
Forward
0 new messages