indexing description: "[ Base class for Widgets. Widgets have an optimal width and height to display their content. Use `resize_to_optimal_dimension' to let the widget set its size. This dimension can be overridden by `set_optimal_dimension'. Each widget has an `is_changed' status. If a widget is not changed, it will not be redrawn! Take into account when creating animated widgets that you will have to call `set_changed' before the widget gets drawn. Redefine `next_frame' to get control every frame and call `set_changed' when a redraw should be made. If you subclass EM_WIDGET directly, you have to provide a `delegate_factory' which has to create new `delegate's for the subclass. Subclasses must call one of 'make_void_surface', 'make_from_surface', 'make_from_dimension' at creation. See EM_COMPONENT, EM_2D_COMPONENT, EM_PANEL ]" date: "$Date$" revision: "$Revision$" deferred class EM_WIDGET inherit EM_2D_COMPONENT redefine make_void_surface, enable, disable, redraw, finish_drawing, mouse_button_down, mouse_button_up, mouse_clicked, mouse_moved, mouse_dragged, mouse_drag_start, mouse_drag_stop, mouse_wheel_down, mouse_wheel_up, mouse_entered, mouse_exited end EM_SHARED_THEME export {NONE} all end feature {NONE} -- Initialisation make_void_surface is -- Initialize widget with a Void surface. -- A call to `set_dimension' will create a surface with the given dimensions. do Precursor {EM_2D_COMPONENT} border := No_border intern_optimal_width := -1 intern_optimal_height := -1 create widgets_impl.make create changed_children.make resize_event.subscribe (agent handle_resize) move_event.subscribe (agent set_changed) hide_event.subscribe (agent set_changed) show_event.subscribe (agent set_changed) if delegate_factory /= Void then delegate_factory.call ([Current]) set_delegate (delegate_factory.last_result) end end delegate_factory: FUNCTION [ANY, TUPLE [], like delegate] is -- Factory to create default delegate deferred end feature -- Access font: EM_FONT -- Font of widget foreground_color: EM_COLOR -- Foreground color of widget background: EM_BACKGROUND -- Background of widget border: EM_BORDER -- Border of widget delegate: EM_WIDGET_DELEGATE -- Delegate which draws widget widgets: DS_LINEAR [EM_WIDGET] is -- Nested widgets. do Result := widgets_impl ensure widgets_not_void: Result /= Void end screen_x: INTEGER is -- X position of `Current' on screen do if parent_widget = Void then Result := x else Result := parent_widget.screen_x + x end end screen_y: INTEGER is -- Y position of `Current' on screen do if parent_widget = Void then Result := y else Result := parent_widget.screen_y + y end end feature -- Measurement inner_width: INTEGER is -- Inner width of `Current' do Result := width - border.left - border.right ensure width_minus_border: Result = width - border.left - border.right end inner_height: INTEGER is -- Inner height of `Current' do Result := height - border.top -border.bottom ensure height_minus_border: Result = height - border.top - border.bottom end optimal_width: INTEGER is -- Optimal width of `Current' do if intern_optimal_width = -1 then if delegate = Void then Result := 0 else Result := delegate.optimal_width (Current) end else Result := intern_optimal_width end end optimal_height: INTEGER is -- Optimal height of `Current' do if intern_optimal_height = -1 then if delegate = Void then Result := 0 else Result := delegate.optimal_height (Current) end else Result := intern_optimal_height end end feature -- Status report is_changed: BOOLEAN -- Is this widget changed since last draw? has_widget (a_widget: EM_WIDGET): BOOLEAN is --Is `a_widget' present on this widget? require a_widget_not_void: a_widget /= Void do Result := widgets_impl.has (a_widget) end feature -- Status setting enable is -- Enable widget. do Precursor {EM_2D_COMPONENT} set_changed ensure then is_changed: is_changed end disable is -- Disable widget. do Precursor {EM_2D_COMPONENT} set_changed ensure then is_changed: is_changed end set_changed is -- Set this widget's changed status to true. -- This also sets the changed status of the parent widget (if any) to true. do is_changed := True if parent_widget /= Void then parent_widget.child_changed (Current) end ensure is_changed: is_changed end set_unchanged is -- Set widget's changed status to false. do is_changed := False ensure not_changed: not is_changed end feature -- Element change set_optimal_dimension (a_width: like optimal_width; a_height: like optimal_height) is -- Set optimal dimension to `a_width' `a_height'. -- The dimenson of the widget will not change, but subsequent calls to `optimal_width' -- and `optimal_height' will return `a_width' and `a_height'. Also a call to `resize_to_optimal_dimension' -- will set the widget's dimension to `a_width' `a_height'. -- This setting will overwrite the optimal dimension definded by the widget delegate. -- If a negative width or height is set, the optimal dimension will be set by the -- delegate again. do if a_width < 0 then intern_optimal_width := -1 else intern_optimal_width := a_width end if a_height < 0 then intern_optimal_height := -1 else intern_optimal_height := a_height end ensure optimal_width_set: a_width >= 0 implies optimal_width = a_width optimal_height_set: a_height >= 0 implies optimal_height = a_height end resize_to_optimal_dimension is -- Set dimension to `optimal_width' `optimal_height'. do set_dimension (optimal_width, optimal_height) set_changed ensure width_set: width = optimal_width height_set: height = optimal_height changed: is_changed end set_font (a_font: like font) is -- Set `font' to `a_font'. require a_font_not_void: a_font /= Void do font := a_font set_changed ensure font_set: font = a_font changed: is_changed end set_foreground_color (a_color: like foreground_color) is -- Set `foreground_color' to `a_color'. require a_color_not_void: a_color /= Void do foreground_color := a_color set_changed ensure foreground_color_set: foreground_color = a_color changed: is_changed end set_background (a_background: like background) is -- Set `background' to `a_background'. do background := a_background set_changed ensure background_set: background = a_background changed: is_changed end set_background_color (a_color: EM_COLOR) is -- Set `background' to display `a_color' as color. -- If a_color has an alpha value of 255, an EM_COLOR_BACKGROUND will be used. -- Otherwise an EM_ALPHA_COLOR_BACKGROUND or an -- EM_TRANSPARENT_BACKGROUND is used. require a_color_not_void: a_color /= Void do if a_color.alpha = 255 then set_background (create {EM_COLOR_BACKGROUND}.make_from_color (a_color)) elseif a_color.alpha = 0 then set_transparent else set_background (create {EM_ALPHA_COLOR_BACKGROUND}.make_from_color (a_color)) end ensure background_set: background /= Void changed: is_changed end set_transparent is -- Set `background' to an EM_TRANSPARENT_BACKGROUND. do set_background (create {EM_TRANSPARENT_BACKGROUND}.make) ensure background_set: background /= Void changed: is_changed end set_transparent_color (a_color: EM_COLOR) is -- Set background to be transparent and use `a_color' as transparent colorkey (this color will be 100% transparent). -- This sets the background to a EM_COLOR_BACKGROUND using `a_color' as color and -- also sets the transparent colorkey of the surface to `a_color' do set_background_color (a_color) surface.set_transparent_color (a_color.red, a_color.green, a_color.blue) ensure surface_colorkey_set: surface.has_transparent_colorkey changed: is_changed end set_border (a_border: like border) is -- Set `border' to `a_border'. -- To clear the border, pass 'Void'. do if a_border = Void then border := No_border else border := a_border end set_changed ensure border_set: a_border /= Void implies border = a_border border_empty: a_border = Void implies border = No_border changed: is_changed end set_delegate (a_delegate: like delegate) is -- Set `delegate' to `a_delegate'. do delegate := a_delegate if delegate /= Void then delegate.install (Current) end ensure delegate_set: delegate = a_delegate end add_widget (a_widget: EM_WIDGET) is -- Add `a_widget' to nested widgets. require a_widget_not_void: a_widget /= Void a_widget_not_present: not widgets.has (a_widget) a_widget_not_current: a_widget /= Current do widgets_impl.put_last (a_widget) changed_children.put_last (a_widget) a_widget.set_parent (Current) set_changed ensure a_widget_added: widgets.has (a_widget) a_widgets_parent_set: a_widget.parent_widget = Current changed: is_changed end feature -- Removal remove_widget (a_widget: EM_WIDGET) is -- Remove `a_widget' from nested widgets. require a_widget_not_void: a_widget /= Void do widgets_impl.delete (a_widget) a_widget.set_parent (Void) set_changed ensure a_widget_removed: not widgets.has (a_widget) a_widgets_parent_set: a_widget.parent_widget = Void changed: is_changed end feature -- Miscellaneous next_frame is -- React on a frame change. -- This can be used to animate a widget. local cursor: DS_LINKED_LIST_CURSOR [EM_WIDGET] do from cursor := widgets_impl.new_cursor cursor.start until cursor.after loop cursor.item.next_frame cursor.forth end end theme_update is -- Update all display data according to new theme. local cursor: DS_LIST_CURSOR [EM_WIDGET] do if delegate_factory /= Void then delegate_factory.call ([Current]) set_delegate (delegate_factory.last_result) end from cursor := widgets_impl.new_cursor cursor.start until cursor.off loop cursor.item.theme_update cursor.forth end end feature -- Drawing redraw is -- Redraw `Current'. do if parent_widget = Void then next_frame end prepare_drawing if is_changed then set_unchanged draw end finish_drawing end draw is -- Draw `Current'. local cursor: DS_LINKED_LIST_CURSOR [EM_WIDGET] widget: EM_WIDGET do if background /= Void then background.draw_on (Current) end if delegate /= Void then delegate.draw_body (Current) end draw_body from cursor := widgets_impl.new_cursor cursor.start until cursor.off loop widget := cursor.item if widget.is_visible then widget.redraw end cursor.forth end changed_children.wipe_out -- End test if border /= No_border then border.draw_on (Current) end end finish_drawing is -- Finish drawing `Current'. do if parent_widget = Void then Precursor {EM_2D_COMPONENT} else parent_widget.surface.blit_surface_part (surface, 0, 0, width, height, x, y) end end draw_body is -- Draw widget body. do end feature {EM_COMPONENT_SCENE, EM_COMPONENT} -- Mouse management mouse_focus: EM_COMPONENT -- Child with mouse focus set_mouse_focus (mouse_x, mouse_y: INTEGER) is -- Set component at position `mouse_x' `mouse_y' to `mouse_focus' -- or set `mouse_focuse' to Void if no component is at the given position. local cursor: DS_LINKED_LIST_CURSOR [EM_WIDGET] widget: EM_WIDGET new_focus: EM_WIDGET do from cursor := widgets_impl.new_cursor cursor.start until cursor.off loop widget := cursor.item if widget.is_visible and widget.x < mouse_x and mouse_x < widget.x + widget.width and widget.y < mouse_y and mouse_y