Page 7 of 36

Whisker Menu 2.7.0 Released

Posted on November 20, 2021, under Whisker Menu

I did not expect to end up making as many changes to Whisker Menu as I did in this release. The menu is pretty well finished at this point, and has been for years. Still! There are always things to do.

Whisker Menu

What’s New?

You now can position the categories horizontally as icons above or below the launchers. This allows for a very Unity-like look to the menu. And you now resize by dragging the sides of the menu instead of the tiny size grip that has annoyed so many users! This makes the menu look more balanced visually as well.

The profile picture is rounded by default to match current trends, but you can set it back to a square if you prefer. Or you can even hide the username and profile picture entirely as has been requested for many years.

I fixed some bugs with the search results, and also improved the result relavancy by only matching as characters in the user-visible name. I also removed the sliding out of search results because of how unpopular it was.

I have replaced gdmflexiserver with dm-tool as the default for switching users. After all, GDM has not provided the old binary for many years. I have also added a new default search action to search for files using catfish.

There are several behind-the-scenes changes as well. I replaced the slots with lambdas, as previously blogged about. I added CSS class names to make theming easier. Stripping of release binaries is now optional. The icon has been renamed to match the new naming scheme of Xfce. And thanks to Matias De lellis, the menu can now use AccountsService for fetching the username and profile picture.

Downloads

Source tarball

SHA-256:
f044056c5325e878873a3a574a65f7c8d3dce2666a0b8345b1eca35bef29dc11

SHA-1:
5b354ef5b9fc9a80e8021c1011eb2dbdf45ff2e7

Revisiting signal handlers once more

Posted on November 14, 2021, under Programming, Whisker Menu

This is a bit of a long post about a purely technical change inside of Whisker Menu. I quite enjoyed solving it, but the change is invisible to end users. All code is under the GNU GPL version 2 or later, same as Whisker Menu.

The adventure begins

I have been reasonably content with how I handle connecting GLib signals to C++ member functions in Whisker Menu. A while ago I even switched back to C++11 and variadic templates as that standard is old enough now that everybody supports it quite well. It makes the code much cleaner behind the scenes.

Recently, though, I was inspired to revisit the signal handlers in Whisker Menu after discovering that the other Xfce plugins written in C++ use lambdas. That was an intriguing idea and got me thinking about how I could do it myself. After all, I like using lambdas in my Qt projects.

Why use lambdas if I am okay with the current code? Well, lambdas let you capture variables, so I don’t need to write extra functions to bind variables. Putting the signal handling code directly next to the signal itself makes it more clear what will happen in response to that signal. And, for the most part, my signal handlers are tiny and having to scroll around in the document to add or modify them is frustrating (especially the settings dialog, which has a ton of one-line signal handlers).

There are still some cases where a lambda will call a separate function, but through the wonders of inlining those calls will frequently be replaced by the compiler with the function they are calling. In fact, looking at the assembly output, the entire lambda is usually inlined in the end, so it is almost like having a C signal handler. But I am getting ahead of myself.

So! How to connect a lambda to a GLib signal?

The first solution

The simplest approach doesn’t require extra shared code!

class Example
{
public:
	Example()
	{
		GtkWidget* widget = gtk_button_new();
		g_signal_connect(G_OBJECT(widget), "button-press-event",
			G_CALLBACK(
				+[](GtkWidget*, GdkEvent*, gpointer user_data) -> gboolean
				{
					Example* example = static_cast<Example*>(user_data);
					… do stuff …
					return GDK_EVENT_PROPAGATE;
				}
			),
			this);
	}
};

Yeah, no. Static casts to get an equivalent "this" pointer" and no binding variables. Yikes. It works, but I don’t think it is very readable. Definitely not something I would want to work with for all the signal handlers in Whisker Menu. This provides nothing over the current approach, and it has definite drawbacks.

Templates to the rescue!

It wasn’t hard to modify my current signal handler helper code to end up with something that stored and called lambdas, but it unfortunately could not deduce the types for me. So I ended up having to write the types out twice. Yuck. It was a good start, but I needed to sit on it for a few days.

enum class Connect
{
	Default = 0,
	After = G_CONNECT_AFTER
};

template<typename R, typename... Args, typename Sender, typename Func>
gulong connect(Sender instance, const gchar* detailed_signal, Func func, Connect flags = Connect::Default)
{
	class Slot
	{
		Func m_func;

	public:
		Slot(Func func) :
			m_func(func)
		{
		}

		static R invoke(Args... args, gpointer user_data)
		{
			return static_cast<Slot*>(user_data)->m_func(args...);
		}

		static void destroy(gpointer data, GClosure*)
		{
			delete static_cast<Slot*>(data);
		}
	};

	return g_signal_connect_data(G_OBJECT(instance),
			detailed_signal,
			G_CALLBACK(&Slot::invoke),
			new Slot(func),
			&Slot::destroy,
			GConnectFlags(flags));
}

class Example()
{
	Example()
	{
		GtkWidget* widget = gtk_button_new();
		connect<gboolean, GtkWidget*, GdkEvent*>(widget, "button-press-event",
			[](GtkWidget*, GdkEvent*) -> gboolean
			{
				… do stuff …
				return GDK_EVENT_PROPAGATE;
			});
	}
};

Solved!

I was certain that the compiler had to be able to find the types itself. Maybe I could make the Slot class a partial specialization? That would allow me to not specify the types in the template of the connect() function. But how to get the compiler to determine the types for the partial specialization?

After some research, I figured it out. Use decltype on the operator() function of the lambda! Since lambdas are basically functors built into the language, they all have an operator() function that is called behind the scenes to make the magic work. And decltype will get me the return type and argument types!

enum class Connect
{
	Default = 0,
	After = G_CONNECT_AFTER
};

template<typename Func, typename T>
class Slot
{
};

template<typename Func, typename R, typename T, typename... Args>
class Slot<Func, R(T::*)(Args...) const>
{
	Func m_func;

public:
	Slot(Func func) :
		m_func(func)
	{
	}

	static R invoke(Args... args, gpointer user_data)
	{
		return static_cast<Slot*>(user_data)->m_func(args...);
	}

	static void destroy(gpointer data, GClosure*)
	{
		delete static_cast<Slot*>(data);
	}
};

template<typename Sender, typename Func>
gulong connect(Sender instance, const gchar* detailed_signal, Func func, Connect flags = Connect::Default)
{
	typedef Slot<Func, decltype(&Func::operator())> Receiver;

	return g_signal_connect_data(G_OBJECT(instance),
			detailed_signal,
			G_CALLBACK(&Receiver::invoke),
			new Receiver(func),
			&Receiver::destroy,
			GConnectFlags(flags));
}

class Example()
{
	Example()
	{
		GtkWidget* widget = gtk_button_new();
		connect(widget, "button-press-event",
			[](GtkWidget*, GdkEvent*) -> gboolean
			{
				… do stuff …
				return GDK_EVENT_PROPAGATE;
			});
	}
};

Astute readers have probably noticed that I am seemingly unnecessarily specifying the return type of the lambdas. It is true that C++ will happily deduce it for me, but I want to make sure that the function signatures exactly match the signal handlers since GLib casts everything to void*.

This was a lot of fun to figure out, and I am quite happy with it. Much happier than I was with the old signal handler code!

Whisker Menu 2.6.2 released

Posted on November 13, 2021, under Whisker Menu

What’s New?

Bug Fixes

  • Fix background shifting when showing menu (Issue #41)
  • Fix menu not toggling after pressing escape (Issue #65)
  • Properly prevent interactive search in treeview

Downloads

Source tarball

SHA-256:
6d20e22c18593aca5adecaf0a7a4f33a6bda233bdd92d3ca7b51c37d0baaf76e

SHA-1:
d92c9cb91dcb731b1f3e6bf23cf6b021804e6b65

Whisker Menu 2.4.6 released

Posted on July 23, 2020, under Whisker Menu

What’s New?

Bug Fixes

  • Fix crash during grab check (Issue #19)
  • Fix background incorrect without compositing

Downloads

Source tarball

SHA-256:
8974d38cc87df528693efe4b6e14bcd233cdb49d2018a23ddddf745110b25744

SHA-1:
267e721e5613456d7956a5fa2adda7c67e98dd47

MD5:
81a4a4c7635273485fac5c2d98e48d02

Whisker Menu 2.4.5 released

Posted on July 21, 2020, under Whisker Menu

What’s New?

Bug Fixes

  • Fix saving plugin title with overridden default text (Bug #16822)
  • Fix extra key press to select search items (Issue #8)
  • Fix icon view skipping first item when pressing arrow key
  • Fix incorrect signal name
  • Fix nonfunctional grab check

Translation Updates

Asturian, Dutch, Esperanto, Hungarian, Icelandic, Portuguese, Portuguese (Brazil), Russian

Downloads

Source tarball

SHA-256:
f5241910ea6411840b8c9f9471f0d262ab0583150bb82f9b280eccbaadb0ebbe

SHA-1:
4d5be0a9c9f8f24604f6fddb9aa6f86069d4cc03

MD5:
adb064538b2e2cbc7ddd1d8ac57cec36

Categories