Opened 6 months ago

Last modified 6 months ago

#18648 new bug

BOptionPopUp sets value even if not available

Reported by: jackburton Owned by: nobody
Priority: normal Milestone: Unscheduled
Component: Kits/Interface Kit Version: R1/beta4
Keywords: Cc:
Blocked By: Blocking:
Platform: All

Description (last modified by jackburton)

With BOptionPopUp::SetValue() you can set a value which does not exist. As you can see from the implementation, it sets the value of the BControl by calling BControl::SetValue(), but later it could return without setting the correct option, since there might be no option with that value.

Calling Value() will then return the newly set value.

void
BOptionPopUp::SetValue(int32 value)
{
	BControl::SetValue(value);
	BMenu* menu = fMenuField->Menu();
	if (menu == NULL)
		return;

	int32 numItems = menu->CountItems();
	for (int32 i = 0; i < numItems; i++) {
		BMenuItem* item = menu->ItemAt(i);
		if (item && item->Message()) {
			int32 itemValue;
			item->Message()->FindInt32("be:value", &itemValue);
			if (itemValue == value) {
				item->SetMarked(true);
				break;
			}
		}
	}
}

If I remember correctly, this behaviour is inherited from BeOS, but maybe it doesn't make much sense, and could be confusing.

Change History (6)

comment:1 by jackburton, 6 months ago

Description: modified (diff)

comment:2 by jackburton, 6 months ago

The proposed change is

void
BOptionPopUp::SetValue(int32 value)
{
-       BControl::SetValue(value);
	BMenu* menu = fMenuField->Menu();
	if (menu == NULL)
		return;

	int32 numItems = menu->CountItems();
	for (int32 i = 0; i < numItems; i++) {
		BMenuItem* item = menu->ItemAt(i);
		if (item && item->Message()) {
			int32 itemValue;
			item->Message()->FindInt32("be:value", &itemValue);
			if (itemValue == value) {
+                        	BControl::SetValue(value);
				item->SetMarked(true);
				break;
			}
		}
	}
}

comment:3 by waddlesplash, 6 months ago

It looks like you were the one to originally implement this class. I don't see any comments either way or in old commit messages...

in reply to:  3 comment:4 by jackburton, 6 months ago

Replying to waddlesplash:

It looks like you were the one to originally implement this class. I don't see any comments either way or in old commit messages...

Yes, I know. Obviously I can't remember for sure if it's the case, but I think that the behaviour is inherited from Beos. Neverthless, it just feels wrong.

comment:5 by pulkomandy, 6 months ago

I will note that other controls behave similarly, for example you can set arbitrary values for a checkbox or button (besides B_CONTROL_ON and B_CONTROL_OFF).

I imagine it can be useful if subclasses add extra things to the control (for example, making a 3-state checkbox where it can be "partially checked").

In the case of BOptionPopUp this could be for example a subclass adding submenus with more options? Or with custom menu items that do more complex things, for example in ACE I have a menu item with a slider inside it, that can take any value. But I think I build it out of BPopupUpMenu rather than BOptionPopUp. So it's probably fine to restrict BOptionPopUp in this way.

Finally: I think this should be done in BOptionControl and not BOptionPopUp? In case we add other subclasses of BOptionControl, they should have the same protection in place. In fact, BOptionControl already has a SelectOptionFor method which does this validation. It even has a comment wondering why this method exist. It seems we just found why? But now we have to find which one should call the other, and make sure we don't end up with recursion.

comment:6 by jackburton, 6 months ago

I'm pretty sure I had tested the Beos behaviour at the time, so I am reasonably sure that the way it's implemented (both BOptionControl and BOptionPopUp) mimics the original behaviour.

Last edited 6 months ago by jackburton (previous) (diff)
Note: See TracTickets for help on using tickets.