You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I had a need to have a menu on Linux with a menu with a sub menu nested inside a sub menu. I'm not proficient in C++ so after I fumbled around for a while, I ultimately came up with this solution. I'll let it in this issue since I doubt this hackery is worthy of a PR. All edits were made inside native_context_menu_plugin_handle_method_call:
staticvoidnative_context_menu_plugin_handle_method_call(
NativeContextMenuPlugin* self, FlMethodCall* method_call) {
g_autoptr(FlMethodResponse) response = nullptr;
const gchar* method = fl_method_call_get_name(method_call);
if (strcmp(method, kShowMenu) == 0) {
// Clear previously saved object instances.
self->last_menu_items.clear();
self->last_menu_item_selected = false;
if (self->last_menu_thread != nullptr) {
self->last_menu_thread->detach();
self->last_menu_thread.reset(nullptr);
}
auto arguments = fl_method_call_get_args(method_call);
auto device_pixel_ratio =
fl_value_lookup_string(arguments, "devicePixelRatio");
auto position = fl_value_lookup_string(arguments, "position");
GdkWindow* window = get_window(self);
GtkWidget* top_level_menu = gtk_menu_new();
auto top_level_items = fl_value_lookup_string(arguments, "items");
for (int32_t i = 0; i < fl_value_get_length(top_level_items); i++) {
int32_t top_level_id = fl_value_get_int(
fl_value_lookup_string(fl_value_get_list_value(top_level_items, i), "id"));
constchar* top_level_title = fl_value_get_string(
fl_value_lookup_string(fl_value_get_list_value(top_level_items, i), "title"));
auto second_level_items =
fl_value_lookup_string(fl_value_get_list_value(top_level_items, i), "items");
self->last_menu_items.emplace_back(std::make_unique<MenuItem>(top_level_id, top_level_title));
GtkWidget* top_level_item = gtk_menu_item_new_with_label(top_level_title);
// Check for second level items & create a second level menu.if (fl_value_get_length(second_level_items) > 0) {
GtkWidget* second_level_menu = gtk_menu_new();
for (int32_t j = 0; j < fl_value_get_length(second_level_items); j++) {
int32_t second_level_id = fl_value_get_int(fl_value_lookup_string(
fl_value_get_list_value(second_level_items, j), "id"));
constchar* second_level_title = fl_value_get_string(fl_value_lookup_string(
fl_value_get_list_value(second_level_items, j), "title"));
auto third_level_items =
fl_value_lookup_string(fl_value_get_list_value(second_level_items, j), "items");
self->last_menu_items.back()->items().emplace_back(
std::make_unique<MenuItem>(second_level_id, second_level_title));
GtkWidget* second_level_item = gtk_menu_item_new_with_label(second_level_title);
// Check for third level items & create a third level menu.if (fl_value_get_length(third_level_items) > 0) {
GtkWidget* third_level_menu = gtk_menu_new();
for (int32_t k = 0; k < fl_value_get_length(third_level_items); k++) {
int32_t third_level_id = fl_value_get_int(fl_value_lookup_string(
fl_value_get_list_value(third_level_items, k), "id"));
constchar* third_level_title = fl_value_get_string(fl_value_lookup_string(
fl_value_get_list_value(third_level_items, k), "title"));
self->last_menu_items.back()->items().back()->items().emplace_back(
std::make_unique<MenuItem>(third_level_id, third_level_title));
GtkWidget* third_level_item = gtk_menu_item_new_with_label(third_level_title);
gtk_widget_show(third_level_item);
gtk_menu_shell_append(GTK_MENU_SHELL(third_level_menu), third_level_item);
g_signal_connect(
G_OBJECT(third_level_item), "activate",
G_CALLBACK(on_menu_item_clicked),
(gpointer)self->last_menu_items.back()->items().at(j).get()->items().at(k).get());
}
gtk_menu_item_set_submenu(GTK_MENU_ITEM(second_level_item), third_level_menu);
} else {
g_signal_connect(G_OBJECT(second_level_item), "activate",
G_CALLBACK(on_menu_item_clicked),
(gpointer)self->last_menu_items.back()->items().at(j).get());
}
gtk_widget_show(second_level_item);
gtk_menu_shell_append(GTK_MENU_SHELL(second_level_menu), second_level_item);
}
gtk_menu_item_set_submenu(GTK_MENU_ITEM(top_level_item), second_level_menu);
} else {
g_signal_connect(G_OBJECT(top_level_item), "activate",
G_CALLBACK(on_menu_item_clicked),
(gpointer)self->last_menu_items.back().get());
}
gtk_widget_show(top_level_item);
gtk_menu_shell_append(GTK_MENU_SHELL(top_level_menu), top_level_item);
}
GdkRectangle rectangle;
// Pass `devicePixelRatio` and `position` from Dart to show menu at// specified coordinates. If it is not defined, WIN32 will use// `GetCursorPos` to show the context menu at the cursor's position.if (device_pixel_ratio != nullptr && position != nullptr) {
rectangle.x = fl_value_get_float(fl_value_get_list_value(position, 0)) *
fl_value_get_float(device_pixel_ratio);
rectangle.y = fl_value_get_float(fl_value_get_list_value(position, 1)) *
fl_value_get_float(device_pixel_ratio);
} else {
GdkDevice* mouse_device;
int x, y;
// Legacy support.
#if GTK_CHECK_VERSION(3, 20, 0)
GdkSeat* seat = gdk_display_get_default_seat(gdk_display_get_default());
mouse_device = gdk_seat_get_pointer(seat);
#else
GdkDeviceManager* devman =
gdk_display_get_device_manager(gdk_display_get_default());
mouse_device = gdk_device_manager_get_client_pointer(devman);
#endifgdk_window_get_device_position(window, mouse_device, &x, &y, NULL);
rectangle.x = x;
rectangle.y = y;
}
g_signal_connect(G_OBJECT(top_level_menu), "deactivate",
G_CALLBACK(on_menu_deactivated), nullptr);
// `gtk_menu_popup_at_rect` is used since `gtk_menu_popup_at_pointer` will// require event box creation & another callback will be involved. This way// is straight forward & easy to work with.// NOTE: GDK_GRAVITY_NORTH_WEST is hard-coded by default since no analog is// present for it inside the Dart platform channel code (as of now). In// summary, this will create a menu whose body is in bottom-right to the// position of the mouse pointer.gtk_menu_popup_at_rect(GTK_MENU(top_level_menu), window, &rectangle,
GDK_GRAVITY_NORTH_WEST, GDK_GRAVITY_NORTH_WEST,
NULL);
// Responding with `null`, click event & respective `id` of the `MenuItem`// is notified through callback. Otherwise the GUI will become unresponsive.// To keep the API same, a `Completer` is used in the Dart.
response =
FL_METHOD_RESPONSE(fl_method_success_response_new(fl_value_new_null()));
} else {
response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new());
}
fl_method_call_respond(method_call, response, nullptr);
}
The text was updated successfully, but these errors were encountered:
I had a need to have a menu on Linux with a menu with a sub menu nested inside a sub menu. I'm not proficient in C++ so after I fumbled around for a while, I ultimately came up with this solution. I'll let it in this issue since I doubt this hackery is worthy of a PR. All edits were made inside
native_context_menu_plugin_handle_method_call
:The text was updated successfully, but these errors were encountered: