Author Topic: create an array of comboboxes  (Read 419 times)

0 Members and 1 Guest are viewing this topic.

Offline Ves

create an array of comboboxes
« on: July 12, 2017, 09:48:08 PM »
Hi all, and hope you enjoy the summer! 8)
I want to create an array of comboboxes in the GUI for the new passenger and mail class system which is used in Extended. I want to create as many comboboxes in a row below each other as there are classes in the vehicle, and the comboxes are used to change the class to another one. The window I am working in is identical to the vehicle_detail_t window.
I have checked how arrays of buttons are created, and translated that to comboboxes:

Code: [Select]
buf.clear();
gui_combobox_t *cs = new gui_combobox_t();
cs->set_pos(scr_coord(pos.x + w + offset.x, pos.y + offset.y + total_height + extra_y));
cs->set_size(scr_size(185, D_BUTTON_HEIGHT));
cs->set_max_size(scr_size(D_BUTTON_WIDTH - 8, LINESPACE * 3 + 2 + 16));
cs->set_highlight_color(1);
cs->clear_elements();
class_indices.clear();
for (uint8 j = 0; j < v->get_desc()->get_number_of_classes(); j++)
{
char class_selector_name_untranslated[32];
sprintf(class_selector_name_untranslated, "p_class[%u]", j);
const char* class_selector_name = translator::translate(class_selector_name_untranslated);
cs->append_element(new gui_scrolled_list_t::const_text_scrollitem_t(translator::translate(class_selector_name), SYSCOL_TEXT));
class_indices.append(j);
}
cont.add_component(cs);
cs->add_listener(this);
cs->set_focusable(false);
extra_y += LINESPACE;

I get a red underline under "this" in cs->add_listener(this); stating that argument of type "gui_class_vehicleinfo_t *" is incompatible with parameter of type "action_listener_t *".
Adding:

Code: [Select]
bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE;
to gui_class_vehicleinfo_t doesnt help either.

I cannot find any existing examples of that in the game, only arrays of buttons and other stuff. I am especially interrested in how to code the ::action_triggered part, as I still dont understand how that corelates with arrays of buttons, or in my case, comboboxes.
Would someone be able to help demonstrate how to make arrays of comboboxes and make them work with ::action_triggered?
« Last Edit: July 18, 2017, 05:02:51 PM by Ves »

Offline prissi

  • Developer
  • Administrator
  • *
  • Posts: 8725
  • Total likes: 303
  • Helpful: 228
  • Languages: De,EN,JP
Re: create an array of scrolled lists
« Reply #1 on: July 13, 2017, 08:51:11 AM »
And array of comboboxes might not be a good idea, as the focus (i.e. combobox) is remembered in order to close it, when clicked outside.

I would rather circle the content of the comboboxes on click.

Offline Ves

Re: create an array of scrolled lists
« Reply #2 on: July 14, 2017, 07:48:35 AM »
Thanks for your reply, what do you mean with that you would rather circle the content of the comboboxes on click? Do you mean that you have one combo box that determines the value that should be changed, and another box that change the actual value?

I might have described badly what I would like: A list of selectors. Similar to the one where you select wether to buy or sell in the depot. I only just don't know how many of them is needed, hence the array.

Offline prissi

  • Developer
  • Administrator
  • *
  • Posts: 8725
  • Total likes: 303
  • Helpful: 228
  • Languages: De,EN,JP
Re: create an array of scrolled lists
« Reply #3 on: July 16, 2017, 03:11:21 PM »
As long as it is a static array, you can put anything into it. But it must be an array to pointers to comboboxes, and you need to check the focus in the deletion routine and close any open first. The dielogue itself must no know of the array, you just add them one by one. Hence if "this" in your extract is the init routine of the dialogue (or a scrolled list, or anything else derived from gui_container_t) then it should work. However, this cannot by the array.

I think the full routine is needed to know what the problem is.

Offline Ves

Re: create an array of comboboxes
« Reply #4 on: July 18, 2017, 08:04:46 PM »
Thanks! James helped me out a bit, so its only the "this" part that doesnt work it seems.

So what you are saying is that I cannot define the comboboxes directly in the array? Should I define ONE combobox in the initial routine and then point to that using pointers?

If it not is too much to ask for, could you take a look at the file in question and maybe give an example of what you describe?
Currently the button is defined around line 352 to 377 in an array in this file: https://github.com/VictorErik/Simutrans-Experimental-Ves/blob/b7b36e1e6865d70a419ad4881f2f40fafd75921b/gui/vehicle_class_manager.cc
The "this" line is commented out currently and compiles as such, but nothing is visible in the game.

Offline TurfIt

Re: create an array of comboboxes
« Reply #5 on: July 19, 2017, 01:07:53 AM »
You're trying to have gui_class_vehicleinfo_t respond to events (actions) created by the combobox gui element, but it's lacking the interface. i.e. The combobox has the interface class action_creator_t but gui_class_vehicleinfo_t is missing the corresponding ifc action_listener_t.

I think what prissi is trying to say is your actual gui_frame_t that's responding to the infowin events needs knowledge of the comboboxes contained within to possibly play some hackey focus games to get the comboboxes to close properly. The depot dialogue has some of that going on...

I can't really figure out what the structure of your dialog is supposed to be from your code... certainly you don't want those combobox manipulations in the ::draw(); Should be in an init(), or update_data(), or action_triggered, or something that's not called every frame.

Offline Ves

Re: create an array of comboboxes
« Reply #6 on: July 19, 2017, 08:31:17 AM »
Thank you very much!
I think I have understood somethings much better now, as I can now get the comboboxes visible in game and show up and disappear when they are supposed to!

Offline Ves

Re: create an array of comboboxes
« Reply #7 on: July 22, 2017, 02:58:17 PM »
I am getting a bit stuck with some things:

First, I struggle to make the content of the scrolled lists show anything sensible. I have defined the array of combo boxes like this:

Code: [Select]
int pass_class_capacity[255] = { 0 };
for (uint8 i = 0; i < goods_manager_t::passengers->get_number_of_classes(); i++)
{
for (unsigned veh = 0; veh < cnv->get_vehicle_count(); veh++)
{
vehicle_t* v = cnv->get_vehicle(veh);
if (v->get_cargo_type()->get_catg_index() == 0)
{
pass_class_capacity[i] += v->get_capacity(i);
}
}
if (pass_class_capacity[i] > 0)
{
gui_combobox_t *class_selector = new gui_combobox_t();
class_selector->set_pos(scr_coord(100, i*LINESPACE));
class_selector->set_size(scr_size(185, D_BUTTON_HEIGHT));
class_selector->set_max_size(scr_size(D_BUTTON_WIDTH - 8, LINESPACE * 3 + 2 + 16));
class_selector->set_highlight_color(1);
class_selector->clear_elements();
class_indices.clear();
for (uint8 j = 0; j < goods_manager_t::passengers->get_number_of_classes(); j++)
{
char class_selector_name_untranslated[32];
sprintf(class_selector_name_untranslated, "p_class[%u]", j);
class_selector->append_element(new gui_scrolled_list_t::const_text_scrollitem_t(translator::translate(class_selector_name_untranslated), SYSCOL_TEXT));
class_indices.append(j);
class_selector->set_selection(j);
}
add_component(class_selector);
class_selector->add_listener(this);
class_selector->set_focusable(false);
class_selectors.append(class_selector);
}
}

... which shows up nicely as expected in the game. The logic tells the window to add a combobox for every class that the convoy holds in total.
However, only the last entry shows the expected value and the rest of the content of the comboboxes shows []. There appears to be the correct number of [] -entries, and the last entry is the anticipated one. If you try to change the value in the combobox, the expected value disappears and only [] shows in its place.
I have imitated the livery selector from experimental which looks like this:

Code: [Select]
livery_selector.add_listener(this);
add_component(&livery_selector);
livery_selector.clear_elements();
vector_tpl<livery_scheme_t*>* schemes = welt->get_settings().get_livery_schemes();
livery_scheme_indices.clear();
ITERATE_PTR(schemes, i)
{
livery_scheme_t* scheme = schemes->get_element(i);
if(scheme->is_available(welt->get_timeline_year_month()))
{
livery_selector.append_element(new gui_scrolled_list_t::const_text_scrollitem_t(translator::translate(scheme->get_name()), SYSCOL_TEXT));
livery_scheme_indices.append(i);
livery_selector.set_selection(i);
livery_scheme_index = i;
}
}
... the main difference being that James has used "ITERATE_PTR" and I have used just a "for(...)".
It seems to me like the entries in my comboboxes overwrite them selfs, why only the last entry is preserved for display. What am I doing wrong?


The other issue I have is that I dont understand how to use the action creator when I have a list of comboboxes (or even buttons for that matter..). When I look at a normal combobox entry, like the livery selector or buy/sell options, it kinds of makes sence to me. However, when looking at array entries, like the destination buttons for the halt_detail window, I dont really understand how the action creator gets which button is pressed. Even less, I would understand how to make the combobox-array entries in there. Could somebody explain this to me or give me an example of how to do it? I have updated the github with the working array of boxes.

Thank you both for your help!

Offline TurfIt

Re: create an array of comboboxes
« Reply #8 on: July 22, 2017, 07:11:57 PM »
I have updated the github with the working array of boxes.
Can't find anything....


However, only the last entry shows the expected value and the rest of the content of the comboboxes shows [].
gui_scrolled_list_t::const_text_scrollitem_t    <- const should be the giveaway on what this expects. A reused string on the stack ain't it.


I dont really understand how the action creator gets which button is pressed.
The action creator *is* the button. It knows where it is, swallows the window event, and calls its listeners when clicked on. Unless you're implementing some new gui component, you don't really need to be concerned with this.
If the listener is listening to multiple creators, then it needs to distinguish who is calling... that's what the first parameter passed in ::action_triggered() is used for. Compare it against your selectors.

Offline Ves

create an array of comboboxes
« Reply #9 on: July 22, 2017, 09:29:27 PM »
Sorry my bad, here is the link (changed gitub branch) https://github.com/VictorErik/Simutrans-Experimental-Ves/tree/passenger-and-mail-classes-gui-combolist

Ok, so I have to create the list of names before I give them to the gui_scrolled_list... ?

The comboboxes are supposed to change some values to the convoy specified, I do need to use the action triggered then?
« Last Edit: July 22, 2017, 09:58:03 PM by Ves »

Offline Ves

Re: create an array of comboboxes
« Reply #10 on: July 23, 2017, 07:45:51 PM »
I found a way to get the entries on the list show up. I dont know if this is failproof in all circumstances but it appears to work:

Code: [Select]
int pass_class_capacity[255] = { 0 };
for (uint8 i = 0; i < goods_manager_t::passengers->get_number_of_classes(); i++)
{
for (unsigned veh = 0; veh < cnv->get_vehicle_count(); veh++)
{
vehicle_t* v = cnv->get_vehicle(veh);
if (v->get_cargo_type()->get_catg_index() == 0)
{
pass_class_capacity[i] += v->get_capacity(i);
}
}
if (pass_class_capacity[i] > 0)
{
gui_combobox_t *class_selector = new gui_combobox_t();
class_selector->set_pos(scr_coord(100, i*LINESPACE));
class_selector->set_size(scr_size(185, D_BUTTON_HEIGHT));
class_selector->set_max_size(scr_size(D_BUTTON_WIDTH - 8, LINESPACE * 3 + 2 + 16));
class_selector->set_highlight_color(1);
class_selector->clear_elements();
class_indices.clear();

for (int j = 1; j < goods_manager_t::passengers->get_number_of_classes()+1; j++)
{
char pass_class_name_untranslated[32][510];
sprintf(pass_class_name_untranslated[j], "p_class[%u]", j-1);
class_selector->append_element(new gui_scrolled_list_t::const_text_scrollitem_t(translator::translate(pass_class_name_untranslated[j]), SYSCOL_TEXT));
class_indices.append(0);
}
add_component(class_selector);
class_selector->add_listener(this);
class_selector->set_focusable(false);
class_selectors.append(class_selector);
}
}
... the difference being this:
Code: [Select]
char pass_class_name_untranslated[32][510];
sprintf(pass_class_name_untranslated[j], "p_class[%u]", j-1);
class_selector->append_element(new gui_scrolled_list_t::const_text_scrollitem_t(translator::translate(pass_class_name_untranslated[j]), SYSCOL_TEXT));
Did I do this right?

Now the action creator still puzles me.
You write that the first parameter is used so I can compare it to my selectors, but how?
From other GUI-windows, like the halt detail with its arrays of line entries etc, it looks like something like this is specified in the beginning:
Code: [Select]
if (v.i&~1)... followed by
Code: [Select]
if(  k.x>=0  ) {}and
Code: [Select]
uint16 j=k.y;which somehow seems to specify precisely which button is is used.

How do I translate that to comboboxes?

Offline TurfIt

Re: create an array of comboboxes
« Reply #11 on: July 23, 2017, 11:37:22 PM »
Did I do this right?
Getting there... think about the scope of your variables - you need something that's valid for the duration of the open window...
(note also if you actually have translation entries for these, then it works. But bad program form to have something that crashes if an external user editable file is not perfect...)


Now the action creator still puzles me.
You write that the first parameter is used so I can compare it to my selectors, but how?
From other GUI-windows, like the halt detail with its arrays of line entries etc, it looks like something like this is specified in the beginning:
I translate that to comboboxes?
That's a bad example to be looking at - it's combining knowledge of how it layed out the buttons with the extra return parameter from button actions to fake it. i.e. In the action_triggered routine it doesn't actually directly know which button object was pressed - if you notice, it doesn't actually do anything with/to the any of the buttons.

Maybe try:
Code: [Select]
bool ai_option_t::action_triggered( gui_action_creator_t *comp, value_t v )
{
if(  comp==&construction_speed  ) {
...
}
else if(  comp==buttons+0  ) {
ai->set_road_transport( !buttons[0].pressed );
buttons[0].pressed = ai->has_road_transport();
}
else if(  comp==buttons+1  ) {
...
}
...
as an example. You have class_selectors as your list rather than buttons...