Help with creating Last Tool shortcut

Repeat Last and Repeat History do what I want, but too much:
1. Get last tool
2. Use last tool exactly the same way (mouse movement etc)

Difference is History gives you a list of previous operations to choose from.

All I want is the last tool, not the performance afterwards. I can have a single key that will give me the last tool used. It is a great thing. It saves my left arm from repeating a zillion multikey shortcuts, and my right arm from repeated mouse movement back and forth through toolbars, pie menus, and quick favorites.

Having trouble finding where the magic happens. The part that says, “First, get the last tool. Second, do a bunch of mousey garbage you don’t want 90% of the time.”

I could either create a new thing called Last Tool or modify Repeat Last. At this point, I don’t even care about the stuff that happens after giving me the previous tool. I use previous tool way more often than previous tool + identical performance. I have no problem sacrificing that for the short term while a more elegant solution is created.

After a few hours of digging, this is where I am:
screen_ops.c — “blender-git\blender\source\blender\editors\screen”

Line 3409 for Repeat Last:
WM_operator_repeat_interactive(C, lastop);

Line 3470 for Repeat History:
WM_operator_repeat(C, op);

These lead to:
wm_event_system.c — “blender-git\blender\source\blender\windowmanager\intern”

Lines 1050-1057:
int WM_operator_repeat(bContext *C, wmOperator *op)
{
	return wm_operator_exec(C, op, true, true, true);
}
int WM_operator_repeat_interactive(bContext *C, wmOperator *op)
{
	return wm_operator_exec(C, op, true, false, true);
}

Lines 952-1004:
static int wm_operator_exec(
		bContext *C, wmOperator *op,
		const bool repeat, const bool use_repeat_op_flag, const bool store)
{
	wmWindowManager *wm = CTX_wm_manager(C);
	int retval = OPERATOR_CANCELLED;

	CTX_wm_operator_poll_msg_set(C, NULL);

	if (op == NULL || op->type == NULL)
		return retval;

	if (0 == WM_operator_poll(C, op->type))
		return retval;

	if (op->type->exec) {
		if (op->type->flag & OPTYPE_UNDO) {
			wm->op_undo_depth++;
		}

		if (repeat && use_repeat_op_flag) {
			op->flag |= OP_IS_REPEAT;
		}
		retval = op->type->exec(C, op);
		OPERATOR_RETVAL_CHECK(retval);
		if (repeat && use_repeat_op_flag) {
			op->flag &= ~OP_IS_REPEAT;
		}

		if (op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm) {
			wm->op_undo_depth--;
		}
	}

	/* XXX Disabled the repeat check to address part 2 of #31840.
		*     Carefully checked all calls to wm_operator_exec and WM_operator_repeat, don't see any reason
		*     why this was needed, but worth to note it in case something turns bad. (mont29) */
	if (retval & (OPERATOR_FINISHED | OPERATOR_CANCELLED) /* && repeat == 0 */)
		wm_operator_reports(C, op, retval, false);

	if (retval & OPERATOR_FINISHED) {
		wm_operator_finished(C, op, repeat, store && wm->op_undo_depth == 0);
	}
	else if (repeat == 0) {
		/* warning: modal from exec is bad practice, but avoid crashing. */
		if (retval & (OPERATOR_FINISHED | OPERATOR_CANCELLED)) {
			WM_operator_free(op);
		}
	}

	return retval | OPERATOR_HANDLED;

}

Where to go from here? I’ve looked a bit at a bunch of similar-ish scripts like wm_event_system.h and wm_event_types.h. Going to go through the #include section(s) again, but I’m already helpless enough.

I don’t even know what’s going on here, but can kinda follow where things lead to. Any help is greatly appreciated.

My guess? Operators have the post-tool mouse movement built into them. Which probably makes a solution for just the tool, without the mouse movement, pretty annoying. Hoping that I’m wrong.

1 Like

You’ll probably want to call something like this:

WM_operator_name_call_ptr(C, op->type, WM_OP_INVOKE_DEFAULT, NULL);

Where you invoke rather than execute the operator, and don’t pass the operator itself but just its type to create a new one.

1 Like

Thanks so much, it is working. Having trouble filtering out unwanted operators like move, scale, rotate, deselect.

static int repeat_tool_exec(bContext *C, wmOperator *UNUSED(op)) {
wmWindowManager *wm = CTX_wm_manager(C);
wmOperator *lastop = wm->operators.last;

/* Seek last registered operator */
while (lastop) {
	if (lastop->type->flag & OPTYPE_REGISTER) {
		if (STRINGIFY(lastop->idname) == "TRANSFORM_OT_translate") {
			printf("\n");
			printf("TRANSFORM_OT_translate is the same as ");
			printf(lastop->idname);
			lastop = lastop->prev;
		}
		else {
			printf("\n");
			printf("TRANSFORM_OT_translate is NOT the same as ");
			printf(lastop->idname);
			break;
		}
	}
	else {
		lastop = lastop->prev;
	}
}

if (lastop) {
	WM_operator_free_all_after(wm, lastop);
	WM_operator_repeat_tool(C, lastop);}

return OPERATOR_CANCELLED;
}

Here is what console gives me:

TRANSFORM_OT_translate is NOT the same as MESH_OT_extrude_vertices_move
TRANSFORM_OT_translate is NOT the same as TRANSFORM_OT_translate

Pretty sure I should be creating a string, setting it to lastop->idname, and comparing that.

string idName = STRINGIFY(lastop->idname);
if (idName != "TRANSFORM_OT_translate") {
    break;
}

Problem is, I can’t use string or I get undeclared identifier errors on build. Tried all kinds of stuff with #include and “using namespace std” but nothing gives. If I include STR_String.h other things throw errors instead.

How do I use strings? I feel like I’m losing my mind or something.

Use STREQ(lastop->idname, "TRANSFORM_OT_translate") instead.

1 Like

Would it be possible to get similar functionality just using Python ?

I dunno but based on what I’ve seen as far as addons I have to figure it could. I have this working but I’m going to revisit it to see what I can do for a history version of it. I have it where it will show a list of previous tools, but it won’t filter it for uniques. If you use the same tool a few times, it will just be a list of one tool over and over.

Haven’t done anything with it since back almost a year ago so it will be interesting to see what if anyting is different now.

1 Like

It would be nice to have a tool that works more along the lines of Maya’s “Repeat Last”. The one in Blender feels somewhat awkward to me in that regard. Mostly because it, as you said, picks up things that you don’t always want to repeat, and it seems to be thrown off easily by other actions.

An alternative Repeat-Last would be nice, one that could exist alongside the current one.
Or maybe Repeat-Last could get a feature that lets the user filter out certain types of actions.

Maybe i missing something and this functionality already exists under a different name ?

Yeah that’s exactly what I was going for, and it works the same as Maya far as I can tell.

WM_operator_name_call_ptr(C, op->type, WM_OP_INVOKE_DEFAULT, NULL);

Is the key part of making it work, doing INVOKE does exactly what I was wanting. That and adding filters for things you don’t want to repeat. The History version of repeat in Blender, where it will show you a list of previous tools is the trickier one I never got quite like I want. But what I got going for that was functional at least.

Gonna go back to this and a few other things I’ve done once I’m done messing around with select-through.

And no, unless somethings changed, or I’ve missed it as well, this Maya type of repeat doesn’t exist in Blender.

1 Like

I wonder if there is a “Feature Requests” thing somewhere where people can vote up feature requests for Blender. I know i would vote “More Industry-Standard like Repeat Last Tool” through the roof :wink:

You can find that over at right click select.

1 Like