WM: Initial Tool System
The tool-system it's self is primitive and may be changed. Adding to 2.8 to develop operators and manipulators as tools. Currently this is exposed in the toolbar, collapsed by default. Work-flow remains unchanged if you don't change the active tool. Placing the 3D cursor is now a Click instead of a Press event, this allows tweak events to be mapped to tools such as border select, keeping click for 3D cursor placement when selection tools are set.
This commit is contained in:
parent
b66728d63d
commit
e1e7b6db2e
|
@ -64,6 +64,12 @@ _modules = [
|
|||
"properties_scene",
|
||||
"properties_texture",
|
||||
"properties_world",
|
||||
|
||||
# Generic Space Modules
|
||||
#
|
||||
# Depends on DNA_WORKSPACE_TOOL (C define).
|
||||
"space_toolsystem_toolbar",
|
||||
|
||||
"space_clip",
|
||||
"space_console",
|
||||
"space_dopesheet",
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# <pep8 compliant>
|
||||
import bpy
|
||||
|
||||
__all__ = (
|
||||
"ToolSelectPanelHelper",
|
||||
)
|
||||
|
||||
|
||||
class ToolSelectPanelHelper:
|
||||
"""
|
||||
Generic Class, can be used for any toolbar.
|
||||
|
||||
- keymap_prefix:
|
||||
The text prefix for each key-map for this spaces tools.
|
||||
- tools_all():
|
||||
Returns all tools defined.
|
||||
- tools_from_context(context):
|
||||
Returns tools available in this context.
|
||||
|
||||
Each tool is a triplet:
|
||||
``(tool_name, manipulator_group_idname, keymap_actions)``
|
||||
For a separator in the toolbar, use ``None``.
|
||||
|
||||
Where:
|
||||
``tool_name``
|
||||
is the name to display in the interface.
|
||||
``manipulator_group_idname``
|
||||
is an optional manipulator group to activate when the tool is set.
|
||||
``keymap_actions``
|
||||
an optional triple of: ``(operator_id, operator_properties, keymap_item_args)``
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def _km_actionmouse_simple(cls, kc, text, actions):
|
||||
|
||||
# standalone
|
||||
def props_assign_recursive(rna_props, py_props):
|
||||
for prop_id, value in py_props.items():
|
||||
if isinstance(value, dict):
|
||||
props_assign_recursive(getattr(rna_props, prop_id), value)
|
||||
else:
|
||||
setattr(rna_props, prop_id, value)
|
||||
|
||||
km_idname = cls.keymap_prefix + text
|
||||
km = kc.keymaps.new(km_idname, space_type=cls.bl_space_type, region_type='WINDOW')
|
||||
for op_idname, op_props_dict, kmi_kwargs in actions:
|
||||
kmi = km.keymap_items.new(op_idname, **kmi_kwargs)
|
||||
kmi_props = kmi.properties
|
||||
if op_props_dict:
|
||||
props_assign_recursive(kmi.properties, op_props_dict)
|
||||
return km, km_idname
|
||||
|
||||
@classmethod
|
||||
def register(cls):
|
||||
wm = bpy.context.window_manager
|
||||
|
||||
# XXX, should we be manipulating the user-keyconfig on load?
|
||||
# Perhaps this should only add when keymap items don't already exist.
|
||||
#
|
||||
# This needs some careful consideration.
|
||||
kc = wm.keyconfigs.user
|
||||
|
||||
# {tool_name: (keymap, keymap_idname, manipulator_group_idname), ...}
|
||||
cls._tool_keymap = {}
|
||||
|
||||
for t in cls.tools_all():
|
||||
text, mp_idname, actions = t
|
||||
if actions is not None:
|
||||
km, km_idname = cls._km_actionmouse_simple(kc, text, actions)
|
||||
cls._tool_keymap[text] = km, km_idname
|
||||
|
||||
def draw(self, context):
|
||||
# XXX, this UI isn't very nice.
|
||||
# We might need to create new button types for this.
|
||||
# Since we probably want:
|
||||
# - tool-tips that include multiple key shortcuts.
|
||||
# - ability to click and hold to expose sub-tools.
|
||||
|
||||
workspace = context.workspace
|
||||
km_idname_active = workspace.tool_keymap or None
|
||||
mp_idname_active = workspace.tool_manipulator_group or None
|
||||
layout = self.layout
|
||||
|
||||
for tool_items in self.tools_from_context(context):
|
||||
if tool_items:
|
||||
col = layout.box().column()
|
||||
for item in tool_items:
|
||||
if item is None:
|
||||
col = layout.box().column()
|
||||
continue
|
||||
text, mp_idname, actions = item
|
||||
|
||||
if actions is not None:
|
||||
km, km_idname = self._tool_keymap[text]
|
||||
else:
|
||||
km = None
|
||||
km_idname = None
|
||||
|
||||
props = col.operator(
|
||||
"wm.tool_set",
|
||||
text=text,
|
||||
emboss=(
|
||||
km_idname_active == km_idname and
|
||||
mp_idname_active == mp_idname
|
||||
),
|
||||
)
|
||||
|
||||
props.keymap = km_idname or ""
|
||||
props.manipulator_group = mp_idname or ""
|
|
@ -0,0 +1,134 @@
|
|||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# <pep8 compliant>
|
||||
|
||||
# For now group all tools together
|
||||
# we may want to move these into per space-type files.
|
||||
#
|
||||
# For now keep this in a single file since it's an area that may change,
|
||||
# so avoid making changes all over the place.
|
||||
|
||||
from bpy.types import Panel
|
||||
|
||||
from .space_toolsystem_common import (
|
||||
ToolSelectPanelHelper,
|
||||
)
|
||||
|
||||
|
||||
class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'TOOLS'
|
||||
bl_category = "Tools"
|
||||
bl_label = "Active Tool (Test)"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
# Satisfy the 'ToolSelectPanelHelper' API.
|
||||
keymap_prefix = "3D View Tool: "
|
||||
|
||||
@classmethod
|
||||
def tools_from_context(cls, context):
|
||||
return (cls._tools[None], cls._tools.get(context.mode, ()))
|
||||
|
||||
@classmethod
|
||||
def tools_all(cls):
|
||||
return [t for t_list in cls._tools.values() for t in t_list if t is not None]
|
||||
|
||||
# Internal Data
|
||||
|
||||
# for reuse
|
||||
_tools_transform = (
|
||||
("Translate", None,
|
||||
(("transform.translate", dict(release_confirm=True), dict(type='EVT_TWEAK_A', value='ANY')),)),
|
||||
("Rotate", None,
|
||||
(("transform.rotate", dict(release_confirm=True), dict(type='EVT_TWEAK_A', value='ANY')),)),
|
||||
("Scale", None,
|
||||
(("transform.resize", dict(release_confirm=True), dict(type='EVT_TWEAK_A', value='ANY')),)),
|
||||
)
|
||||
|
||||
_tools = {
|
||||
None: [
|
||||
("Cursor", None,
|
||||
(("view3d.cursor3d", dict(), dict(type='ACTIONMOUSE', value='CLICK')),)),
|
||||
("Select Border", None, (
|
||||
("view3d.select_border", dict(deselect=False), dict(type='EVT_TWEAK_A', value='ANY')),
|
||||
("view3d.select_border", dict(deselect=True), dict(type='EVT_TWEAK_A', value='ANY', ctrl=True)),
|
||||
)),
|
||||
("Select Circle", None, (
|
||||
("view3d.select_circle", dict(deselect=False), dict(type='ACTIONMOUSE', value='PRESS')),
|
||||
("view3d.select_circle", dict(deselect=True), dict(type='ACTIONMOUSE', value='PRESS', ctrl=True)),
|
||||
)),
|
||||
("Select Lasso", None, (
|
||||
("view3d.select_lasso",
|
||||
dict(deselect=False), dict(type='EVT_TWEAK_A', value='ANY')),
|
||||
("view3d.select_lasso",
|
||||
dict(deselect=True), dict(type='EVT_TWEAK_A', value='ANY', ctrl=True)),
|
||||
)),
|
||||
],
|
||||
'OBJECT': [
|
||||
*_tools_transform,
|
||||
],
|
||||
'POSE': [
|
||||
*_tools_transform,
|
||||
],
|
||||
'EDIT_ARMATURE': [
|
||||
*_tools_transform,
|
||||
("Roll", None, (
|
||||
("transform.transform",
|
||||
dict(release_confirm=True, mode='BONE_ROLL'),
|
||||
dict(type='EVT_TWEAK_A', value='ANY')),
|
||||
)),
|
||||
None,
|
||||
("Extrude Cursor", None,
|
||||
(("armature.click_extrude", dict(), dict(type='ACTIONMOUSE', value='PRESS')),)),
|
||||
],
|
||||
'EDIT_MESH': [
|
||||
*_tools_transform,
|
||||
None,
|
||||
("Rip Region", None, (
|
||||
("mesh.rip_move", dict(TRANSFORM_OT_translate=dict(release_confirm=True)),
|
||||
dict(type='ACTIONMOUSE', value='PRESS')),
|
||||
)),
|
||||
("Rip Edge", None, (
|
||||
("mesh.rip_edge_move", dict(TRANSFORM_OT_translate=dict(release_confirm=True)),
|
||||
dict(type='ACTIONMOUSE', value='PRESS')),
|
||||
)),
|
||||
("Knife", None, (("mesh.knife_tool", dict(wait_for_input=False), dict(type='ACTIONMOUSE', value='PRESS')),)),
|
||||
("Bisect", None, (("mesh.bisect", dict(), dict(type='EVT_TWEAK_A', value='ANY')),)),
|
||||
("Extrude Cursor", None,
|
||||
(("mesh.dupli_extrude_cursor", dict(), dict(type='ACTIONMOUSE', value='PRESS')),)),
|
||||
],
|
||||
'EDIT_CURVE': [
|
||||
*_tools_transform,
|
||||
None,
|
||||
("Draw", None,
|
||||
(("curve.draw", dict(wait_for_input=False), dict(type='ACTIONMOUSE', value='PRESS')),)),
|
||||
("Extrude Cursor", None,
|
||||
(("curve.vertex_add", dict(), dict(type='ACTIONMOUSE', value='PRESS')),)),
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
classes = (
|
||||
VIEW3D_PT_tools_active,
|
||||
)
|
||||
|
||||
if __name__ == "__main__": # only for live edit.
|
||||
from bpy.utils import register_class
|
||||
for cls in classes:
|
||||
register_class(cls)
|
|
@ -238,7 +238,7 @@ void view3d_keymap(wmKeyConfig *keyconf)
|
|||
/* only for region 3D window */
|
||||
keymap = WM_keymap_find(keyconf, "3D View", SPACE_VIEW3D, 0);
|
||||
|
||||
WM_keymap_verify_item(keymap, "VIEW3D_OT_cursor3d", ACTIONMOUSE, KM_PRESS, 0, 0);
|
||||
WM_keymap_verify_item(keymap, "VIEW3D_OT_cursor3d", ACTIONMOUSE, KM_CLICK, 0, 0);
|
||||
|
||||
WM_keymap_verify_item(keymap, "VIEW3D_OT_rotate", MIDDLEMOUSE, KM_PRESS, 0, 0);
|
||||
WM_keymap_verify_item(keymap, "VIEW3D_OT_move", MIDDLEMOUSE, KM_PRESS, KM_SHIFT, 0);
|
||||
|
|
|
@ -50,6 +50,16 @@
|
|||
# endif
|
||||
#endif
|
||||
|
||||
/* Currently testing, allow to disable. */
|
||||
#define USE_WORKSPACE_TOOL
|
||||
|
||||
typedef struct bToolDef {
|
||||
/* either the keymap AND/OR manipulator_group must be defined. */
|
||||
char keymap[64];
|
||||
char manipulator_group[64];
|
||||
int spacetype;
|
||||
int _pad;
|
||||
} bToolDef;
|
||||
|
||||
/**
|
||||
* \brief Wrapper for bScreen.
|
||||
|
@ -80,6 +90,9 @@ typedef struct WorkSpace {
|
|||
int object_mode DNA_PRIVATE_WORKSPACE; /* enum eObjectMode */
|
||||
int flags DNA_PRIVATE_WORKSPACE; /* enum eWorkSpaceFlags */
|
||||
|
||||
/* should be: '#ifdef USE_WORKSPACE_TOOL'. */
|
||||
bToolDef tool;
|
||||
|
||||
struct SceneLayer *render_layer DNA_PRIVATE_WORKSPACE;
|
||||
|
||||
char engine_id[32]; /* Render Engine. */
|
||||
|
|
|
@ -146,6 +146,16 @@ static void rna_def_workspace(BlenderRNA *brna)
|
|||
RNA_def_property_ui_text(prop, "Mode", "Object interaction mode");
|
||||
#endif
|
||||
|
||||
prop = RNA_def_property(srna, "tool_keymap", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_string_sdna(prop, NULL, "tool.keymap");
|
||||
RNA_def_property_ui_text(prop, "Active Tool", "Currently active tool keymap");
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
|
||||
prop = RNA_def_property(srna, "tool_manipulator_group", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_string_sdna(prop, NULL, "tool.manipulator_group");
|
||||
RNA_def_property_ui_text(prop, "Active Tool", "Currently active tool manipulator");
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
|
||||
prop = RNA_def_property(srna, "orientations", PROP_COLLECTION, PROP_NONE);
|
||||
RNA_def_property_collection_sdna(prop, NULL, "transform_orientations", NULL);
|
||||
RNA_def_property_struct_type(prop, "TransformOrientation");
|
||||
|
|
|
@ -2722,9 +2722,42 @@ void wm_event_do_handlers(bContext *C)
|
|||
wm_drags_check_ops(C, event);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef USE_WORKSPACE_TOOL
|
||||
/* How to solve properly?
|
||||
*
|
||||
* Handlers are stored in each region,
|
||||
* however the tool-system swaps keymaps often and isn't stored
|
||||
* per region.
|
||||
*
|
||||
* Need to investigate how this could be done better.
|
||||
* We might need to add a more dynamic handler type that uses a callback
|
||||
* to fetch its current keymap.
|
||||
*/
|
||||
wmEventHandler sneaky_handler = {NULL};
|
||||
if (ar->regiontype == RGN_TYPE_WINDOW) {
|
||||
WorkSpace *workspace = WM_window_get_active_workspace(win);
|
||||
if (workspace->tool.keymap[0] &&
|
||||
workspace->tool.spacetype == sa->spacetype)
|
||||
{
|
||||
wmKeyMap *km = WM_keymap_find_all(
|
||||
C, workspace->tool.keymap, sa->spacetype, RGN_TYPE_WINDOW);
|
||||
if (km != NULL) {
|
||||
sneaky_handler.keymap = km;
|
||||
BLI_addhead(&ar->handlers, &sneaky_handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* USE_WORKSPACE_TOOL */
|
||||
|
||||
action |= wm_handlers_do(C, event, &ar->handlers);
|
||||
|
||||
#ifdef USE_WORKSPACE_TOOL
|
||||
if (sneaky_handler.keymap) {
|
||||
BLI_remlink(&ar->handlers, &sneaky_handler);
|
||||
}
|
||||
#endif /* USE_WORKSPACE_TOOL */
|
||||
|
||||
/* fileread case (python), [#29489] */
|
||||
if (CTX_wm_window(C) == NULL)
|
||||
return;
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
#include "DNA_scene_types.h"
|
||||
#include "DNA_userdef_types.h"
|
||||
#include "DNA_windowmanager_types.h"
|
||||
#include "DNA_workspace_types.h"
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
|
@ -1743,6 +1744,60 @@ static void WM_OT_operator_defaults(wmOperatorType *ot)
|
|||
ot->flag = OPTYPE_INTERNAL;
|
||||
}
|
||||
|
||||
#ifdef USE_WORKSPACE_TOOL
|
||||
/* ***************** Set Active Tool ************************* */
|
||||
|
||||
/* Developers note: in it's current form this doesn't need to be an operator,
|
||||
* keep this as-is for now since it may end up setting an active key-map.
|
||||
*/
|
||||
|
||||
static int wm_operator_tool_set_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
WorkSpace *workspace = CTX_wm_workspace(C);
|
||||
ScrArea *sa = CTX_wm_area(C);
|
||||
char id_keymap[sizeof(workspace->tool.keymap)];
|
||||
char id_manipulator_group[sizeof(workspace->tool.manipulator_group)];
|
||||
RNA_string_get(op->ptr, "keymap", id_keymap);
|
||||
RNA_string_get(op->ptr, "manipulator_group", id_manipulator_group);
|
||||
|
||||
if (workspace->tool.manipulator_group[0]) {
|
||||
wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(workspace->tool.manipulator_group, false);
|
||||
if (wgt != NULL) {
|
||||
wmManipulatorMapType *mmap_type = WM_manipulatormaptype_ensure(&wgt->mmap_params);
|
||||
WM_manipulatormaptype_group_unlink(C, bmain, mmap_type, wgt);
|
||||
}
|
||||
}
|
||||
|
||||
/* NOTE: we may want to move this logic into a function. */
|
||||
{
|
||||
BLI_strncpy(workspace->tool.keymap, id_keymap, sizeof(workspace->tool.keymap));
|
||||
BLI_strncpy(workspace->tool.manipulator_group, id_manipulator_group, sizeof(workspace->tool.manipulator_group));
|
||||
workspace->tool.spacetype = sa->spacetype;
|
||||
}
|
||||
|
||||
if (workspace->tool.manipulator_group[0]) {
|
||||
WM_manipulator_group_type_ensure(workspace->tool.manipulator_group);
|
||||
}
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static void WM_OT_tool_set(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Set Active Tool";
|
||||
ot->idname = "WM_OT_tool_set";
|
||||
ot->description = "Set the active tool";
|
||||
|
||||
ot->exec = wm_operator_tool_set_exec;
|
||||
|
||||
ot->flag = OPTYPE_INTERNAL;
|
||||
|
||||
RNA_def_string(ot->srna, "keymap", NULL, KMAP_MAX_NAME, "Key Map", "");
|
||||
RNA_def_string(ot->srna, "manipulator_group", NULL, MAX_NAME, "Manipulator Group", "");
|
||||
}
|
||||
#endif /* USE_WORKSPACE_TOOL */
|
||||
|
||||
/* ***************** Splash Screen ************************* */
|
||||
|
||||
static void wm_block_splash_close(bContext *C, void *arg_block, void *UNUSED(arg))
|
||||
|
@ -3604,6 +3659,9 @@ void wm_operatortype_init(void)
|
|||
WM_operatortype_append(WM_OT_memory_statistics);
|
||||
WM_operatortype_append(WM_OT_debug_menu);
|
||||
WM_operatortype_append(WM_OT_operator_defaults);
|
||||
#ifdef USE_WORKSPACE_TOOL
|
||||
WM_operatortype_append(WM_OT_tool_set);
|
||||
#endif
|
||||
WM_operatortype_append(WM_OT_splash);
|
||||
WM_operatortype_append(WM_OT_search_menu);
|
||||
WM_operatortype_append(WM_OT_call_menu);
|
||||
|
|
Loading…
Reference in New Issue