Mercurial > octave
changeset 24523:501986e12b8b
Implement "pickableparts" property (bug #52795).
* NEWS: Announce that property "PickableParts" has been implemented.
* gendpropdoc.m: Document property. State that the property does nothing for
figure and root objects.
* graphics.in.h: Add allowed value "all" to base_properties::pickableparts and
remove pickableparts property from all other objects.
* gl-render.h (opengl_renderer::selecting): Add new boolean attribute and
setter.
* gl-render.cc (opengl_renderer::draw_axes): While in selection mode don't draw
axes if "pickableparts" is "none".
(opengl_renderer::draw_axes_x/y/zgrid, opengl_renderer::draw_axes_plane):
While in selection mode, allow drawing grids/plane if "pickableparts" is "all"
(opengl_renderer::draw_line): While in selection mode, allow drawing line and
markers "pickableparts" is "all".
(opengl_renderer::draw_patch): While in selection mode, allow drawing lines
polygons and markers if "pickableparts" is "all".
(opengl_renderer::draw_surface): ditto.
(opengl_renderer::draw_all_lights): while in selection mode, allow drawing
children objects if "pickableparts" is "all".
* gl-select.cc (opengl_selector::draw): Put renderer in selection mode.
* Canvas.cc (Canvas::canvasMousePressEvent): overhaul handling of currentObj.
author | Pantxo Diribarne <pantxo.diribarne@gmail.com> |
---|---|
date | Wed, 03 Jan 2018 21:21:41 +0100 |
parents | ec5591efafe4 |
children | a56d283ff18a |
files | NEWS doc/interpreter/genpropdoc.m libgui/graphics/Canvas.cc libgui/graphics/gl-select.cc libinterp/corefcn/gl-render.cc libinterp/corefcn/gl-render.h libinterp/corefcn/graphics.in.h |
diffstat | 7 files changed, 147 insertions(+), 85 deletions(-) [+] |
line wrap: on
line diff
--- a/NEWS Thu Jan 04 14:25:11 2018 -0800 +++ b/NEWS Wed Jan 03 21:21:41 2018 +0100 @@ -63,6 +63,9 @@ explicitly instructed to perform an economy factorization by using a final argument of 0. + ** The graphic object property "PickableParts" has been implemented which + controls whether an object can accept mouse clicks. + ** Text objects now implement the properties "BackgroundColor", "EdgeColor", "LineStyle", "LineWidth", and "Margin".
--- a/doc/interpreter/genpropdoc.m Thu Jan 04 14:25:11 2018 -0800 +++ b/doc/interpreter/genpropdoc.m Wed Jan 03 21:21:41 2018 +0100 @@ -165,11 +165,30 @@ handle is not visible in its parent's \"children\" property."; case "hittest" + s.doc = "Specify whether __objname__ processes mouse events \ +or passes them to ancestors of the object. When enabled, the object may \ +respond to mouse clicks by evaluating the @qcode{\"buttondownfcn\"}, showing \ +the uicontextmenu, and eventually becoming the root \ +@qcode{\"currentobject\"}. This property is only relevant when the object \ +can accept mouse clicks which is determined by the @qcode{\"pickableparts\"} \ +property. @xref{XREF__objname__pickableparts, , @w{pickableparts property}}."; + case "interruptible" case "parent" s.doc = "Handle of the parent graphics object."; s.valid = valid_handle; + case "pickableparts" + s.doc = "Specify whether __objname__ will accept mouse clicks. \ +By default, __prop__ is @qcode{\"visible\"} and only visible parts of the \ +__objname__ or its children may react to mouse clicks. When __prop__ is \ +@qcode{\"all\"} both visible and invisible parts (or children) may react to \ +mouse clicks. When __prop__ is @qcode{\"none\"} mouse clicks on the object \ +are ignored and transmitted to any objects underneath this one. When an \ +object is configured to accept mouse clicks the @qcode{\"hittest\"} property \ +will determine how they are processed. \ +@xref{XREF__objname__hittest, , @w{hittest property}}."; + case "selected" case "selectionhighlight" case "tag" @@ -210,6 +229,12 @@ s.doc = "Root figure has no parent graphics object. __prop__ \ is always empty."; + case "hittest" + s.doc = doc_unused; + + case "pickableparts" + s.doc = doc_unused; + ## Specific properties case "callbackobject" s.doc = "Graphics handle of the current object whose callback is executing."; @@ -296,6 +321,9 @@ case "clipping" s.doc = doc_unused; + case "pickableparts" + s.doc = doc_unused; + ## Specific properties case "alphamap" s.doc = sprintf (doc_notimpl, "Transparency"); @@ -721,9 +749,6 @@ @xref{XREFaxesposition, , @w{position property}}."; s.valid = valid_4elvec; - case "pickableparts" - s.doc = doc_unused; - case "plotboxaspectratio" s.doc = "@xref{XREFpbaspect, , pbaspect function}. \ __modemsg__.";
--- a/libgui/graphics/Canvas.cc Thu Jan 04 14:25:11 2018 -0800 +++ b/libgui/graphics/Canvas.cc Wed Jan 03 21:21:41 2018 +0100 @@ -617,6 +617,7 @@ { graphics_object figObj (obj.get_ancestor ("figure")); + // Any click in a figure canvas makes it current if (figObj) { graphics_object root = gh_manager::get_object (0); @@ -626,8 +627,16 @@ graphics_object currentObj, axesObj; + // Retrieve selected object. select_object (obj, event, currentObj, axesObj); + // currentObj may be invalid if, e.g., all objects under the mouse + // click had "hittest" -> "off" or "pickableparts" -> "none". In that + // case, replace with underlying figObj which always accepts mouse + // clicks. + if (! currentObj.valid_object ()) + currentObj = figObj; + if (axesObj) { if (axesObj.get_properties ().handlevisibility_is ("on") @@ -635,20 +644,8 @@ && axesObj.get_properties ().get_tag () != "colorbar") Utils::properties<figure> (figObj) .set_currentaxes (axesObj.get_handle ().as_octave_value ()); - if (! currentObj) - currentObj = axesObj; } - if (! currentObj) - currentObj = obj; - - if (currentObj.get_properties ().handlevisibility_is ("on")) - Utils::properties<figure> (figObj) - .set_currentobject (currentObj.get_handle ().as_octave_value ()); - else - Utils::properties<figure> (figObj).set_currentobject ( - octave::numeric_limits<double>::NaN ()); - Figure *fig = dynamic_cast<Figure *> (Backend::toolkitObject (figObj)); MouseMode newMouseMode = NoMode; @@ -659,33 +656,38 @@ switch (newMouseMode) { case NoMode: - gh_manager::post_set (figObj.get_handle (), "selectiontype", - Utils::figureSelectionType (event, isdblclick), false); - - updateCurrentPoint (figObj, obj, event); - - gh_manager::post_callback (figObj.get_handle (), - "windowbuttondownfcn", - button_number (event)); + { + // Update the figure "currentobject" + auto& fprop = Utils::properties<figure> (figObj); + + if (currentObj.get_properties ().handlevisibility_is ("on")) + fprop.set_currentobject (currentObj.get_handle () + .as_octave_value ()); + else + fprop.set_currentobject (Matrix ()); + + // Update figure "selectiontype" and "currentpoint" + gh_manager::post_set ( + figObj.get_handle (), "selectiontype", + Utils::figureSelectionType (event, isdblclick), false); - if (currentObj.get ("buttondownfcn").isempty ()) - { - graphics_object parentObj = - gh_manager::get_object (currentObj.get_parent ()); + updateCurrentPoint (figObj, obj, event); - if (parentObj.valid_object () && parentObj.isa ("hggroup")) - gh_manager::post_callback (parentObj.get_handle (), - "buttondownfcn", - button_number (event)); - } - else - gh_manager::post_callback (currentObj.get_handle (), - "buttondownfcn", + gh_manager::post_callback (figObj.get_handle (), + "windowbuttondownfcn", button_number (event)); - if (event->button () == Qt::RightButton) - ContextMenu::executeAt (currentObj.get_properties (), - event->globalPos ()); + // Execute the "buttondownfcn" of the selected object + if (! currentObj.get ("buttondownfcn").isempty ()) + gh_manager::post_callback (currentObj.get_handle (), + "buttondownfcn", + button_number (event)); + + // Show context menu of the selected object + if (event->button () == Qt::RightButton) + ContextMenu::executeAt (currentObj.get_properties (), + event->globalPos ()); + } break; case TextMode:
--- a/libgui/graphics/gl-select.cc Thu Jan 04 14:25:11 2018 -0800 +++ b/libgui/graphics/gl-select.cc Wed Jan 03 21:21:41 2018 +0100 @@ -140,7 +140,9 @@ object_map[name] = go; glPushName (name); + set_selecting (true); opengl_renderer::draw (go, toplevel); + set_selecting (false); glPopName (); }
--- a/libinterp/corefcn/gl-render.cc Thu Jan 04 14:25:11 2018 -0800 +++ b/libinterp/corefcn/gl-render.cc Wed Jan 03 21:21:41 2018 +0100 @@ -625,7 +625,8 @@ opengl_renderer::opengl_renderer (void) : toolkit (), xform (), xmin (), xmax (), ymin (), ymax (), zmin (), zmax (), xZ1 (), xZ2 (), marker_id (), filled_marker_id (), - camera_pos (), camera_dir (), interpreter ("none"), txt_renderer () + camera_pos (), camera_dir (), interpreter ("none"), txt_renderer (), + selecting (false) { // This constructor will fail if we don't have OpenGL or if the data // types we assumed in our public interface aren't compatible with the @@ -1389,7 +1390,9 @@ int xstate = props.get_xstate (); - if (props.is_visible () && xstate != AXE_DEPTH_DIR) + if (xstate != AXE_DEPTH_DIR + && (props.is_visible () + || (selecting && props.pickableparts_is ("all")))) { int zstate = props.get_zstate (); bool x2Dtop = props.get_x2Dtop (); @@ -1570,7 +1573,9 @@ int ystate = props.get_ystate (); - if (ystate != AXE_DEPTH_DIR && props.is_visible ()) + if (ystate != AXE_DEPTH_DIR && props.is_visible () + && (props.is_visible () + || (selecting && props.pickableparts_is ("all")))) { int zstate = props.get_zstate (); bool y2Dright = props.get_y2Dright (); @@ -1750,7 +1755,9 @@ { int zstate = props.get_zstate (); - if (zstate != AXE_DEPTH_DIR && props.is_visible ()) + if (zstate != AXE_DEPTH_DIR && props.is_visible () + && (props.is_visible () + || (selecting && props.pickableparts_is ("all")))) { bool xySym = props.get_xySym (); bool zSign = props.get_zSign (); @@ -1960,14 +1967,17 @@ { graphics_object go = gh_manager::get_object (children(i)); - if (go.get_properties ().is_visible ()) + base_properties p = go.get_properties (); + + if (p.is_visible () + || (selecting && p.pickableparts_is ("all"))) { - if (go.isa ("light")) + if (go.isa ("light") && ! selecting) { if (num_lights < max_lights) { current_light = GL_LIGHT0 + num_lights; - set_clipping (go.get_properties ().is_clipping ()); + set_clipping (p.is_clipping ()); draw (go); num_lights++; } @@ -1976,9 +1986,10 @@ "light: Maximum number of lights (%d) in these axes is " "exceeded.", max_lights); } - else if (go.isa ("hggroup")) + else if (go.isa ("hggroup") + && ! (selecting && p.pickableparts_is ("none"))) draw_all_lights (go.get_properties (), obj_list); - else + else if (! (selecting && p.pickableparts_is ("none"))) obj_list.push_back (go); } } @@ -2090,6 +2101,11 @@ if (! props.is_visible () && props.get_tag () == "legend") return; + // Don't draw the axes and its children if we are in selection and + // pickable parts is "none". + if (selecting && props.pickableparts_is ("none")) + return; + static double floatmax = std::numeric_limits<float>::max (); double x_min = props.get_x_min (); @@ -2155,6 +2171,8 @@ { #if defined (HAVE_OPENGL) + bool draw_all = selecting && props.pickableparts_is ("all"); + Matrix x = xform.xscale (props.get_xdata ().matrix_value ()); Matrix y = xform.yscale (props.get_ydata ().matrix_value ()); Matrix z = xform.zscale (props.get_zdata ().matrix_value ()); @@ -2251,11 +2269,15 @@ { Matrix lc, fc; - if (props.markeredgecolor_is ("auto")) + if (draw_all) + lc = Matrix (1, 3, 0.0); + else if (props.markeredgecolor_is ("auto")) lc = props.get_color_rgb (); else if (! props.markeredgecolor_is ("none")) lc = props.get_markeredgecolor_rgb (); + if (draw_all) + fc = Matrix (1, 3, 0.0); if (props.markerfacecolor_is ("auto")) fc = props.get_color_rgb (); else if (! props.markerfacecolor_is ("none")) @@ -2294,6 +2316,8 @@ { #if defined (HAVE_OPENGL) + bool draw_all = selecting && props.pickableparts_is ("all"); + const Matrix x = xform.xscale (props.get_xdata ().matrix_value ()); const Matrix y = xform.yscale (props.get_ydata ().matrix_value ()); const Matrix z = xform.zscale (props.get_zdata ().matrix_value ()); @@ -2378,7 +2402,7 @@ if (fc_mode == TEXTURE) tex = opengl_texture::create (props.get_color_data ()); - if (! props.facecolor_is ("none")) + if (draw_all || ! props.facecolor_is ("none")) { if (fa_mode == 0) { @@ -2835,11 +2859,13 @@ // FIXME: check what to do with marker facecolor set to auto // and facecolor set to none. - bool do_edge = ! props.markeredgecolor_is ("none"); - bool do_face = ! props.markerfacecolor_is ("none"); - - Matrix mecolor = props.get_markeredgecolor_rgb (); - Matrix mfcolor = props.get_markerfacecolor_rgb (); + bool do_edge = draw_all || ! props.markeredgecolor_is ("none"); + bool do_face = draw_all || ! props.markerfacecolor_is ("none"); + + Matrix mecolor = (draw_all ? Matrix (1, 3, 0.0) : + props.get_markeredgecolor_rgb ()); + Matrix mfcolor = (draw_all ? Matrix (1, 3, 0.0) : + props.get_markerfacecolor_rgb ()); Matrix cc (1, 3, 0.0); if (mecolor.isempty () && props.markeredgecolor_is ("auto")) @@ -2922,6 +2948,7 @@ return; } + bool draw_all = selecting && props.pickableparts_is ("all"); const Matrix f = props.get_faces ().matrix_value (); const Matrix v = xform.scale (props.get_vertices ().matrix_value ()); Matrix c; @@ -2940,7 +2967,7 @@ bool has_normals = (n.rows () == nv); int fc_mode = ((props.facecolor_is ("none") - || props.facecolor_is_rgb ()) ? 0 : + || props.facecolor_is_rgb () || draw_all) ? 0 : (props.facecolor_is ("flat") ? 1 : 2)); int fl_mode = (props.facelighting_is ("none") ? 0 : (props.facelighting_is ("flat") ? 1 : 2)); @@ -2989,21 +3016,24 @@ count_f(i) = count; } - if (fc_mode > 0 || ec_mode > 0) + if (draw_all || fc_mode > 0 || ec_mode > 0) { - c = props.get_color_data ().matrix_value (); + if (draw_all) + c = Matrix (1, 3, 0.0); + else + c = props.get_color_data ().matrix_value (); if (c.rows () == 1) { // Single color specifications, we can simplify a little bit - if (fc_mode > 0) + if (draw_all || fc_mode > 0) { fcolor = c; fc_mode = UNIFORM; } - if (ec_mode > 0) + if (draw_all || ec_mode > 0) { ecolor = c; ec_mode = UNIFORM; @@ -3077,7 +3107,7 @@ if (fl_mode > 0 || el_mode > 0) glMaterialf (LIGHT_MODE, GL_SHININESS, se); - if (! props.facecolor_is ("none")) + if (draw_all || ! props.facecolor_is ("none")) { // FIXME: adapt to double-radio property if (fa_mode == 0) @@ -3176,7 +3206,8 @@ } } - if (! props.edgecolor_is ("none") && ! props.linestyle_is ("none")) + if (draw_all + || (! props.edgecolor_is ("none") && ! props.linestyle_is ("none"))) { // FIXME: adapt to double-radio property if (props.get_edgealpha_double () == 1) @@ -3310,13 +3341,15 @@ && ! (props.markeredgecolor_is ("none") && props.markerfacecolor_is ("none"))) { - bool do_edge = ! props.markeredgecolor_is ("none"); - bool do_face = ! props.markerfacecolor_is ("none"); - - Matrix mecolor = props.get_markeredgecolor_rgb (); - Matrix mfcolor = props.get_markerfacecolor_rgb (); - - bool has_markerfacecolor = false; + bool do_edge = draw_all || ! props.markeredgecolor_is ("none"); + bool do_face = draw_all || ! props.markerfacecolor_is ("none"); + + Matrix mecolor = (draw_all ? Matrix (1, 3, 0.0) : + props.get_markeredgecolor_rgb ()); + Matrix mfcolor = (draw_all ? Matrix (1, 3, 0.0) : + props.get_markerfacecolor_rgb ()); + + bool has_markerfacecolor = draw_all || false; if ((mecolor.isempty () && ! props.markeredgecolor_is ("none")) || (mfcolor.isempty () && ! props.markerfacecolor_is ("none")))
--- a/libinterp/corefcn/gl-render.h Thu Jan 04 14:25:11 2018 -0800 +++ b/libinterp/corefcn/gl-render.h Wed Jan 03 21:21:41 2018 +0100 @@ -86,20 +86,24 @@ virtual void init_gl_context (bool enhanced, const Matrix& backgroundColor); virtual void setup_opengl_transformation (const axes::properties& props); + virtual void set_clipbox (double x1, double x2, double y1, double y2, + double z1, double z2); + virtual void set_clipping (bool on); + virtual void set_font (const base_properties& props); virtual void set_color (const Matrix& c); - virtual void set_polygon_offset (bool on, float offset = 0.0f); + virtual void set_interpreter (const caseless_str& interp) + { + interpreter = interp; + } virtual void set_linewidth (float w); virtual void set_linestyle (const std::string& s, bool stipple = false, double linewidth = 0.5); virtual void set_linecap (const std::string&) { }; virtual void set_linejoin (const std::string&) { }; - virtual void set_clipbox (double x1, double x2, double y1, double y2, - double z1, double z2); - virtual void set_clipping (bool on); - virtual void set_font (const base_properties& props); - virtual void set_interpreter (const caseless_str& interp) + virtual void set_polygon_offset (bool on, float offset = 0.0f); + virtual void set_selecting (bool on) { - interpreter = interp; + selecting = on; } virtual void init_marker (const std::string& m, double size, float width); @@ -216,6 +220,8 @@ unsigned int current_light; int max_lights; + // Indicate we are drawing for selection purpose + bool selecting; private: class patch_tesselator; };
--- a/libinterp/corefcn/graphics.in.h Thu Jan 04 14:25:11 2018 -0800 +++ b/libinterp/corefcn/graphics.in.h Wed Jan 03 21:21:41 2018 +0100 @@ -2366,7 +2366,7 @@ bool_property hittest , "on" bool_property interruptible , "on" handle_property parent fs , p - radio_property pickableparts , "{visible}|none" + radio_property pickableparts , "{visible}|all|none" bool_property selected , "off" bool_property selectionhighlight , "on" string_property tag s , "" @@ -3158,9 +3158,6 @@ callback_property windowscrollwheelfcn , Matrix () radio_property windowstyle , "{normal}|modal|docked" - // Base properties which don't exist on object - // radio_property pickableparts h , "{visible}|none" - // Octave-specific properties mutable string_property __gl_extensions__ hr , "" mutable string_property __gl_renderer__ hr , "" @@ -3639,7 +3636,6 @@ radio_property minorgridlinestyle , "{:}|-|--|-.|none" radio_property nextplot , "{replace}|add|replacechildren" array_property outerposition u , default_axes_outerposition () - radio_property pickableparts , "{visible}|all|none" row_vector_property plotboxaspectratio mu , Matrix (1, 3, 1.0) radio_property plotboxaspectratiomode u , "{auto}|manual" array_property position u , default_axes_position () @@ -4313,7 +4309,6 @@ color_property markeredgecolor , color_property (radio_values ("{auto}|none"), color_values (0, 0, 0)) color_property markerfacecolor , color_property (radio_values ("auto|{none}"), color_values (0, 0, 0)) double_property markersize , 6 - radio_property pickableparts , "{visible}|all|none" row_vector_property xdata u , default_data () string_property xdatasource , "" row_vector_property ydata u , default_data () @@ -4429,7 +4424,6 @@ radio_property linestyle , "{-}|--|:|-.|none" double_property linewidth , 0.5 double_property margin , 2 - radio_property pickableparts , "{visible}|all|none" array_property position smu , Matrix (1, 3, 0.0) double_property rotation mu , 0 text_label_property string u , "" @@ -4595,7 +4589,6 @@ array_property cdata u , default_image_cdata () radio_property cdatamapping al , "scaled|{direct}" radio_property erasemode h , "{normal}|none|xor|background" - radio_property pickableparts , "{visible}|all|none" row_vector_property xdata mu , Matrix () row_vector_property ydata mu , Matrix () // hidden properties for limit computation @@ -4883,7 +4876,6 @@ color_property markerfacecolor , color_property (radio_values ("{none}|auto|flat"), color_values (0, 0, 0)) double_property markersize , 6 radio_property normalmode hsg , "{auto}|manual" - radio_property pickableparts , "{visible}|all|none" double_property specularcolorreflectance , 1.0 double_property specularexponent , 10.0 double_property specularstrength , 0.9 @@ -5096,7 +5088,6 @@ double_property markersize , 6 radio_property meshstyle , "{both}|row|column" radio_property normalmode hsg , "{auto}|manual" - radio_property pickableparts , "{visible}|all|none" double_property specularcolorreflectance , 1 double_property specularexponent , 10 double_property specularstrength , 0.9