Bug: input changed event triggers twice

Bug: input changed event triggers twice

rolandas_vegis
Advocate Advocate
758 Views
5 Replies
Message 1 of 6

Bug: input changed event triggers twice

rolandas_vegis
Advocate
Advocate

Hello,

 

After recent Fusion 360 update I noticed a new bug, didn't try all the inputs, but I can reproduce with button row.

When user clicks on a button row item and input changed events triggers, if I change the button row selected item in the event, the event then triggers again and it shows that the selected item did not change - remained the same as the user clicked. After the second event, the button row shows the selected item as the one set from the code. So the problem is that the event triggers twice.

 

Sample code showing the issue (this is edited command inputs sample code):

#include <Core/CoreAll.h>
#include <Fusion/FusionAll.h>

using namespace adsk::core;
using namespace adsk::fusion;

Ptr<Application> app;
Ptr<UserInterface> ui;

// InputChange event handler.
class OnInputChangedEventHander : public adsk::core::InputChangedEventHandler
{
public:
	void notify(const Ptr<InputChangedEventArgs>& eventArgs) override
	{
		Ptr<CommandInputs> inputs = eventArgs->inputs();
		if (!inputs)
			return;

		Ptr<CommandInput> cmdInput = eventArgs->input();
		if (!cmdInput)
			return;

		if (Ptr<ButtonRowCommandInput> buttonRow = cmdInput)
		{
			const auto selectedItem = buttonRow->selectedItem();
			const auto name = selectedItem->name();
			if (name == "Item 2")
			{
				Application::get()->userInterface()->messageBox("Selected item: " + name);

				//Set first item as selected
				const auto firstItem = buttonRow->listItems()->item(0);
				firstItem->isSelected(true);
			}
		}
	}
};

// CommandCreated event handler.
class CommandCreatedEventHandler : public adsk::core::CommandCreatedEventHandler
{
public:
	void notify(const Ptr<CommandCreatedEventArgs>& eventArgs) override
	{
		if (eventArgs)
		{
			// Get the command that was created.
			Ptr<Command> command = eventArgs->command();
			if (command)
			{
				// Connect to the input changed event.
				Ptr<InputChangedEvent> onInputChanged = command->inputChanged();
				if (!onInputChanged)
					return;
				bool isOk = onInputChanged->add(&onInputChangedHandler);
				if (!isOk)
					return;

				// Get the CommandInputs collection associated with the command.
				Ptr<CommandInputs> inputs = command->commandInputs();
				if (!inputs)
					return;

				// Create single selectable button row input.
				Ptr<ButtonRowCommandInput> buttonRowInput = command->commandInputs()->addButtonRowCommandInput("buttonRow", "Button Row 1", false);
				if (!buttonRowInput)
					return;
				Ptr<ListItems> buttonRowItems = buttonRowInput->listItems();
				if (!buttonRowItems)
					return;
				buttonRowItems->add("Item 1", true, "resources/One");
				buttonRowItems->add("Item 2", false, "resources/Two");				
			}
		}
	}
private:
	OnInputChangedEventHander onInputChangedHandler;
} _cmdCreatedHandler;


extern "C" XI_EXPORT bool run(const char* context)
{
	app = Application::get();
	if (!app)
		return false;

	ui = app->userInterface();
	if (!ui)
		return false;

	// Create the command definition.
	Ptr<CommandDefinitions> commandDefinitions = ui->commandDefinitions();
	if (!commandDefinitions)
		return nullptr;

	// Get the existing command definition or create it if it doesn't already exist.
	Ptr<CommandDefinition> cmdDef = commandDefinitions->itemById("cmdInputsSample");
	if (!cmdDef)
	{
		cmdDef = commandDefinitions->addButtonDefinition("cmdInputsSample",
			"Command Inputs Sample",
			"Sample to demonstrate various command inputs.");
	}

	// Connect to the command created event.
	Ptr<CommandCreatedEvent> commandCreatedEvent = cmdDef->commandCreated();
	if (!commandCreatedEvent)
		return false;
	commandCreatedEvent->add(&_cmdCreatedHandler);

	// Execute the command definition.
	cmdDef->execute();

	// Prevent this module from being terminated when the script returns, because we are waiting for event handlers to fire.
	adsk::autoTerminate(false);

	return true;
}
0 Likes
759 Views
5 Replies
Replies (5)
Message 2 of 6

kandennti
Mentor
Mentor

Hi @rolandas_vegis .

 

I can't write C++, but I can read a little.

 

In the InputChangedEventHandler, when you click on "Item 2", you yourself write it so that it switches to "Item 1".
InputChangedEvent is not only fired when you click on a dialog, but also when you switch in the API.
This is not a bug, but normal behavior.

 

Here is the python code I tested.

# Fusion360API Python script

import traceback
import adsk.fusion
import adsk.core

_app: adsk.core.Application = None
_ui: adsk.core.UserInterface = None
_handlers = []

class MyInputChangedHandler(adsk.core.InputChangedEventHandler):
    def __init__(self):
        super().__init__()

    def notify(self, args: adsk.core.InputChangedEventArgs):
        adsk.core.Application.get().log(args.firingEvent.name)

        if args.input.id != 'buttonRow':
            return

        buttonRow: adsk.core.ButtonRowCommandInput = args.input
        name = buttonRow.selectedItem.name
        if name == 'Item 2':
            firstItem = buttonRow.listItems.item(0)
            firstItem.isSelected = True


class MyCommandCreatedHandler(adsk.core.CommandCreatedEventHandler):
    def __init__(self):
        super().__init__()

    def notify(self, args: adsk.core.CommandCreatedEventArgs):
        adsk.core.Application.get().log(args.firingEvent.name)
        try:
            global _handlers
            cmd: adsk.core.Command = adsk.core.Command.cast(args.command)
            inputs: adsk.core.CommandInputs = cmd.commandInputs

            onDestroy = MyCommandDestroyHandler()
            cmd.destroy.add(onDestroy)
            _handlers.append(onDestroy)

            onInputChanged = MyInputChangedHandler()
            cmd.inputChanged.add(onInputChanged)
            _handlers.append(onInputChanged)

            buttonRowInput: adsk.core.ButtonRowCommandInput = inputs.addButtonRowCommandInput(
                'buttonRow',
                'Button Row 1',
                False
            )
            buttonRowItems: adsk.core.ListItems = buttonRowInput.listItems
            buttonRowItems.add("Item 1", True, "resources/One")
            buttonRowItems.add("Item 2", False, "resources/Two")

        except:
            _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))


class MyCommandDestroyHandler(adsk.core.CommandEventHandler):
    def __init__(self):
        super().__init__()

    def notify(self, args: adsk.core.CommandEventArgs):
        adsk.core.Application.get().log(args.firingEvent.name)
        adsk.terminate()


def run(context):
    try:
        global _app, _ui
        _app = adsk.core.Application.get()
        _ui = _app.userInterface

        cmdDef: adsk.core.CommandDefinition = _ui.commandDefinitions.itemById(
            'test_cmd'
        )

        if not cmdDef:
            cmdDef = _ui.commandDefinitions.addButtonDefinition(
                'test_cmd',
                'Test',
                'Test'
            )

        global _handlers
        onCommandCreated = MyCommandCreatedHandler()
        cmdDef.commandCreated.add(onCommandCreated)
        _handlers.append(onCommandCreated)

        cmdDef.execute()

        adsk.autoTerminate(False)

    except:
        if _ui:
            _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
0 Likes
Message 3 of 6

rolandas_vegis
Advocate
Advocate
I disagree that this is normal. When the event fires second time, the selected item in the event is not the one set from code. Code change the selected item to Item 1, however in the event it gives that the selected item is Item 2, which is incorrect.
0 Likes
Message 4 of 6

kandennti
Mentor
Mentor

@rolandas_vegis .

 

You write Item1 to be in the selected state when Item2 is selected.
At that time a second InputChangedEvent is called.

1.png

0 Likes
Message 5 of 6

rolandas_vegis
Advocate
Advocate
Did you try to run my code?

What you say is correct, but as I said - the second event also shows that selected item is Item 2, which is wrong.
0 Likes
Message 6 of 6

kandennti
Mentor
Mentor

@rolandas_vegis .

 

I am not running your code.
I have not installed Visual Studio because I do not use C++.

0 Likes