indexing description: "[ A context menu that can hold items of type EM_WIDGET. Adapted to work with EM_LABEL, EM_CHECKBOX and EM_BUTTON. You get a Submenu with EM_MENU_ITEM. ]" date: "$Date$" revision: "$Revision$" class EM_CONTEXT_MENU inherit EM_LIST [EM_WIDGET] export {NONE} add_widget redefine mouse_exited, mouse_moved, put end create make_empty, make_from_menu feature {NONE} -- Initialisation make_empty is -- Initialise empty context menu. do make_list make_void_surface disable hide end make_from_menu (a_menu: EM_CONTEXT_MENU) is -- Initialise list with elements from `a_menu'. local cursor: DS_LINEAR_CURSOR [EM_WIDGET] do make_empty from cursor := a_menu.elements_impl.new_cursor cursor.start until cursor.off loop put (cursor.item) cursor.forth end end delegate_factory: FUNCTION [ANY, TUPLE [], like delegate] is -- Factory to create default delegate do Result := theme_delegates.CONTEXT_MENU_delegate_factory end feature -- Access selection_background_color: EM_COLOR -- Background on selected element selection_foreground_color: EM_COLOR -- Foreground on selected element parent_menu_item: EM_MENU_ITEM feature -- Element change put (a_widget: EM_WIDGET) is -- puts `a_widget' in the context menu and adapts all -- inserted widgets to the same size local old_optimal_width: INTEGER cursor: DS_LINEAR_CURSOR [EM_WIDGET] a_menu_item: EM_MENU_ITEM do precursor {EM_LIST} (a_widget) old_optimal_width := optimal_width add_widget (a_widget) a_widget.set_position (5, height) if a_widget.width <= old_optimal_width - 5 then a_widget.set_width (old_optimal_width - 5) else -- widget is larger than old_optimal_width, resize all others from cursor := elements_impl.new_cursor cursor.start until cursor.off loop cursor.item.set_width (optimal_width - 5) cursor.forth end end -- resize context menu resize_to_optimal_dimension -- every button down event on a inserted widget will close the menu a_menu_item ?= a_widget -- except for a menu_item if a_menu_item = void then a_widget.mouse_clicked_event.subscribe (agent handle_item_clicked (?)) end ensure then width_set: a_widget.width = optimal_width - 5 end set_selection_background_color (a_color: like selection_background_color) is -- set the background color of the selected element to `a_color'. require a_color_not_void: a_color /= Void do selection_background_color := a_color set_changed ensure selection_background_color_set: selection_background_color = a_color changed: is_changed end set_selection_foreground_color (a_color: like selection_foreground_color) is -- set the foreground color of the selected element to `a_color'. require a_color_not_void: a_color /= Void do selection_foreground_color := a_color set_changed ensure selection_foreground_color_set: selection_foreground_color = a_color changed: is_changed end set_parent_menu_item (a_menu_item: EM_MENU_ITEM) is -- do parent_menu_item := a_menu_item end feature -- Menu Management show_menu is -- show the entire context menu local cursor: DS_LINEAR_CURSOR [EM_WIDGET] a_menu_item: EM_MENU_ITEM do -- first we hide all, that nothing will be at the wrong position -- if someone press the right mousebutton and the context menu is open from cursor := elements_impl.new_cursor cursor.start until cursor.off loop a_menu_item ?= cursor.item if a_menu_item /= void and then a_menu_item.menu.is_visible then a_menu_item.hide_menu end cursor.forth end -- now show and enable the context menu -- we check, if the menu fits on the screen, else its position -- is being adjusted: -- to the left side of the mousepointer if screen_x + width > video_subsystem.video_surface_width then set_position (x - width, y) end -- and above the mousepointer if screen_y + height > video_subsystem.video_surface_height then set_position (x, y - height) end from cursor.start until cursor.off loop cursor.item.enable cursor.forth end -- set the mouse focus on current context menu -- necessary to prevent focus problems mouse_focus := void show enable ensure is_visible: is_visible is_enabled: is_enabled not_over_screen: screen_x + width < video_subsystem.video_surface_width and screen_y + height < video_subsystem.video_surface_height end hide_menu is -- hide the context menu and its opened children local cursor: DS_LINEAR_CURSOR [EM_WIDGET] a_menu_item: EM_MENU_ITEM do hide unselect disable -- do it for its children context menu too from cursor := elements_impl.new_cursor cursor.start until cursor.off loop a_menu_item ?= cursor.item if a_menu_item /= void and a_menu_item.menu.is_visible then -- we found a sub context menu and we will close it a_menu_item.hide_menu end cursor.item.disable cursor.forth end ensure is_not_enabled: not is_enabled hidden: not is_visible nothing_selected: not has_selected_element end feature {NONE} -- Mouse Management mouse_exited is -- context menu loses focus local a_menu_item: EM_MENU_ITEM do -- we don't call the precursor a_menu_item ?= shared_mouse_focus.current_focus if a_menu_item = void then unselect -- maybe here precursor end end mouse_moved (event: EM_MOUSEMOTION_EVENT) is -- catch the mouse event if it moves over the context menu -- important for selection, see delegates local moved_index: INTEGER a_list: EM_LIST [EM_WIDGET] do a_list ?= Current moved_index := delegate.position_to_text_index (a_list, event.x, event.y) if moved_index <= 0 or moved_index > current.count then unselect else -- only set new selected index if it has changed if selected_index /= moved_index then set_selected_index (moved_index) end end -- so we have a focus on the selected element -- shared_mouse_focus.set_current_focus (selected_element) -- and to pass the event precursor (event) end feature {NONE} -- Implementation handle_item_clicked (event: EM_MOUSEBUTTON_EVENT) is -- if a context menu's item has been pressed we handle it here do -- here goes a cascaded close over all open menues cascaded_close end feature {EM_CONTEXT_MENU, EM_MENU_ITEM} -- Implementation cascaded_close is -- used to close all menues above and below current menu_item do -- a parent menu is open if parent_menu_item /= void and parent_menu_item.is_visible then parent_menu_item.cascaded_close end hide_menu end end