Remove the toggle semantics from all the selection stuff. Now

left-clicking always selects, and there's a special context menu
item to deselect. Also streamline the right-click behavior by
making that select the hovered item, and making all the context
menu items work on the selection.

[git-p4: depot-paths = "//depot/solvespace/": change = 2091]
This commit is contained in:
Jonathan Westhues 2010-01-03 02:26:15 -08:00
parent ee052b556a
commit a4a1d3b619
6 changed files with 124 additions and 132 deletions

View File

@ -164,9 +164,9 @@ void GraphicsWindow::PasteClipboard(Vector trans, double theta, double scale) {
}
cr->newReq = hr;
ToggleSelectionStateOf(hr.entity(0));
MakeSelected(hr.entity(0));
for(i = 0; i < pts; i++) {
ToggleSelectionStateOf(hr.entity(i+1));
MakeSelected(hr.entity(i+1));
}
}

View File

@ -94,12 +94,18 @@ void GraphicsWindow::ClearNonexistentSelectionItems(void) {
}
//-----------------------------------------------------------------------------
// Is this entity selected?
// Is this entity/constraint selected?
//-----------------------------------------------------------------------------
bool GraphicsWindow::EntityIsSelected(hEntity he) {
bool GraphicsWindow::IsSelected(hEntity he) {
Selection s;
ZERO(&s);
s.entity = he;
return IsSelected(&s);
}
bool GraphicsWindow::IsSelected(Selection *st) {
Selection *s;
for(s = selection.First(); s; s = selection.NextAfter(s)) {
if(s->entity.v == he.v) {
if(s->Equals(st)) {
return true;
}
}
@ -107,16 +113,18 @@ bool GraphicsWindow::EntityIsSelected(hEntity he) {
}
//-----------------------------------------------------------------------------
// Toggle the selection state of the indicated item: if it was selected then
// un-select it, and if it wasn't then select it.
// Unselect an item, if it is selected. We can either unselect just that item,
// or also unselect any coincident points. The latter is useful if the user
// somehow selects two coincident points (like with select all), because it
// would otherwise be impossible to de-select the lower of the two.
//-----------------------------------------------------------------------------
void GraphicsWindow::ToggleSelectionStateOf(hEntity he, bool batch) {
void GraphicsWindow::MakeUnselected(hEntity he, bool coincidentPointTrick) {
Selection stog;
ZERO(&stog);
stog.entity = he;
ToggleSelectionStateOf(&stog, batch);
MakeUnselected(&stog, coincidentPointTrick);
}
void GraphicsWindow::ToggleSelectionStateOf(Selection *stog, bool batch) {
void GraphicsWindow::MakeUnselected(Selection *stog, bool coincidentPointTrick){
if(stog->IsEmpty()) return;
Selection *s;
@ -127,14 +135,12 @@ void GraphicsWindow::ToggleSelectionStateOf(Selection *stog, bool batch) {
for(s = selection.First(); s; s = selection.NextAfter(s)) {
if(s->Equals(stog)) {
s->tag = 1;
wasSelected = true;
break;
}
}
// If two points are coincident, then it's impossible to hover one of
// them. But make sure to deselect both, to avoid mysterious seeming
// inability to deselect if the bottom one did somehow get selected.
if(wasSelected && stog->entity.v && !batch) {
if(stog->entity.v && coincidentPointTrick) {
Entity *e = SK.GetEntity(stog->entity);
if(e->IsPoint()) {
Vector ep = e->PointGetNum();
@ -149,20 +155,27 @@ void GraphicsWindow::ToggleSelectionStateOf(Selection *stog, bool batch) {
}
}
}
selection.RemoveTagged();
}
// It's too confusing to make operations that select multiple entities
// (like marquee selection) toggle. So make those select-only.
if(!batch) {
selection.RemoveTagged();
}
if(wasSelected) return;
// So it's not selected, so we should select it.
//-----------------------------------------------------------------------------
// Select an item, if it isn't selected already.
//-----------------------------------------------------------------------------
void GraphicsWindow::MakeSelected(hEntity he) {
Selection stog;
ZERO(&stog);
stog.entity = he;
MakeSelected(&stog);
}
void GraphicsWindow::MakeSelected(Selection *stog) {
if(stog->IsEmpty()) return;
if(IsSelected(stog)) return;
if(stog->entity.v != 0 && SK.GetEntity(stog->entity)->IsFace()) {
// In the interest of speed for the triangle drawing code,
// only two faces may be selected at a time.
int c = 0;
Selection *s;
selection.ClearTags();
for(s = selection.First(); s; s = selection.NextAfter(s)) {
hEntity he = s->entity;
@ -202,7 +215,7 @@ void GraphicsWindow::SelectByMarquee(void) {
if(pp.x >= xmin && pp.x <= xmax &&
pp.y >= ymin && pp.y <= ymax)
{
ToggleSelectionStateOf(e->h, true);
MakeSelected(e->h);
}
} else {
// Use the 3d bounding box test routines, to avoid duplication;
@ -224,7 +237,7 @@ void GraphicsWindow::SelectByMarquee(void) {
!ptA.OutsideAndNotOn(ptMax, ptMin) ||
!ptB.OutsideAndNotOn(ptMax, ptMin))
{
ToggleSelectionStateOf(e->h, true);
MakeSelected(e->h);
break;
}
}

View File

@ -633,7 +633,7 @@ void GraphicsWindow::MenuEdit(int id) {
if(e->IsFace() || e->IsDistance()) continue;
if(!e->IsVisible()) continue;
SS.GW.ToggleSelectionStateOf(e->h, true);
SS.GW.MakeSelected(e->h);
}
InvalidateGraphics();
SS.later.showTW = true;
@ -675,15 +675,15 @@ void GraphicsWindow::MenuEdit(int id) {
}
}
if(onChain && !alreadySelected) {
SS.GW.ToggleSelectionStateOf(e->h, true);
SS.GW.MakeSelected(e->h);
newlySelected++;
didSomething = true;
}
}
} while(didSomething);
if(newlySelected == 0) {
Error("No entities share endpoints with the selected "
"entities.");
Error("No additional entities share endpoints with the "
"selected entities.");
}
InvalidateGraphics();
SS.later.showTW = true;

167
mouse.cpp
View File

@ -177,21 +177,21 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
pending.normal = hover.entity;
pending.operation = DRAGGING_NORMAL;
} else {
if(EntityIsSelected(e->h)) {
// The entity is selected, which means that it wasn't
// before the user clicked to start dragging, which
if(!hoverWasSelectedOnMousedown) {
// The user clicked an unselected entity, which
// means they're dragging just the hovered thing,
// not the full selection. So clear all the selection
// except that entity.
ClearSelection();
ToggleSelectionStateOf(e->h);
MakeSelected(e->h);
}
StartDraggingBySelection();
// If something's hovered, then the user selected it when
// they clicked to start dragging, but they probably
// didn't mean to select it. Or if it was selected, then
// they didn't mean to deselect it. So fix that.
ToggleSelectionStateOf(e->h);
if(!hoverWasSelectedOnMousedown) {
// And then clear the selection again, since they
// probably didn't want that selected if they just
// were dragging it.
ClearSelection();
}
hover.Clear();
pending.operation = DRAGGING_POINTS;
}
@ -214,7 +214,7 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
if(hover.entity.v) {
// Avoid accidentally selecting workplanes when
// starting drags.
ToggleSelectionStateOf(hover.entity);
MakeUnselected(hover.entity, false);
hover.Clear();
}
pending.operation = DRAGGING_MARQUEE;
@ -500,121 +500,95 @@ void GraphicsWindow::MouseRightUp(double x, double y) {
context.active = true;
if(!hover.IsEmpty()) {
MakeSelected(&hover);
SS.later.showTW = true;
}
GroupSelection();
if(hover.IsEmpty() &&
gs.n == 0 &&
gs.constraints == 0 &&
(SS.clipboard.r.n == 0 || !LockedInWorkplane()))
{
// No reason to display a context menu.
goto done;
}
// We can either work on the selection (like the functions are designed to)
// or on the hovered item. In the latter case we can fudge things by just
// selecting the hovered item, and then applying our operation to the
// selection.
bool selEmpty = false;
if(gs.n == 0 && gs.constraints == 0) {
selEmpty = true;
}
bool itemsSelected = (gs.n > 0 || gs.constraints > 0);
if(selEmpty) {
if(hover.IsStylable()) {
if(itemsSelected) {
if(gs.stylables > 0) {
ContextMenuListStyles();
AddContextMenuItem("Hovered: Assign to Style", CONTEXT_SUBMENU);
AddContextMenuItem("Assign to Style", CONTEXT_SUBMENU);
}
if(!hover.IsEmpty()) {
AddContextMenuItem("Hovered: Group Info", CMNU_GROUP_INFO);
if(gs.n + gs.constraints == 1) {
AddContextMenuItem("Group Info", CMNU_GROUP_INFO);
}
if(hover.IsStylable()) {
AddContextMenuItem("Hovered: Style Info", CMNU_STYLE_INFO);
if(gs.n + gs.constraints == 1 && gs.stylables == 1) {
AddContextMenuItem("Style Info", CMNU_STYLE_INFO);
}
if(hover.constraint.v) {
Constraint *c = SK.GetConstraint(hover.constraint);
if(gs.withEndpoints > 0) {
AddContextMenuItem("Select Edge Chain", CMNU_SELECT_CHAIN);
}
if(gs.constraints == 1 && gs.n == 0) {
Constraint *c = SK.GetConstraint(gs.constraint[0]);
if(c->HasLabel() && c->type != Constraint::COMMENT) {
AddContextMenuItem("Hovered: Toggle Reference Dimension",
AddContextMenuItem("Toggle Reference Dimension",
CMNU_REFERENCE_DIM);
}
if(c->type == Constraint::ANGLE ||
c->type == Constraint::EQUAL_ANGLE)
{
AddContextMenuItem("Hovered: Other Supplementary Angle",
AddContextMenuItem("Other Supplementary Angle",
CMNU_OTHER_ANGLE);
}
}
if(hover.entity.v) {
Entity *p = SK.GetEntity(hover.entity);
if(p->IsPoint()) {
Constraint *c;
IdList<Constraint,hConstraint> *lc = &(SK.constraint);
for(c = lc->First(); c; c = lc->NextAfter(c)) {
if(c->type != Constraint::POINTS_COINCIDENT) continue;
if(c->ptA.v == p->h.v || c->ptB.v == p->h.v) {
break;
}
}
if(c) {
AddContextMenuItem(
"Hovered: Delete Point-Coincident Constraint",
CMNU_DEL_COINCIDENT);
if(gs.comments > 0 || gs.points > 0) {
AddContextMenuItem("Snap to Grid", CMNU_SNAP_TO_GRID);
}
if(gs.points == 1) {
Entity *p = SK.GetEntity(gs.point[0]);
Constraint *c;
IdList<Constraint,hConstraint> *lc = &(SK.constraint);
for(c = lc->First(); c; c = lc->NextAfter(c)) {
if(c->type != Constraint::POINTS_COINCIDENT) continue;
if(c->ptA.v == p->h.v || c->ptB.v == p->h.v) {
break;
}
}
}
if(hover.HasEndpoints()) {
AddContextMenuItem("Hovered: Select Edge Chain", CMNU_SELECT_CHAIN);
}
if((hover.constraint.v &&
SK.GetConstraint(hover.constraint)->type == Constraint::COMMENT) ||
(hover.entity.v &&
SK.GetEntity(hover.entity)->IsPoint()))
{
AddContextMenuItem("Hovered: Snap to Grid", CMNU_SNAP_TO_GRID);
}
if(!hover.IsEmpty()) {
AddContextMenuItem(NULL, CONTEXT_SEPARATOR);
AddContextMenuItem("Delete Hovered Item", CMNU_DELETE_SEL);
}
} else {
if(gs.stylables > 0) {
ContextMenuListStyles();
AddContextMenuItem("Selected: Assign to Style", CONTEXT_SUBMENU);
}
if(gs.n + gs.constraints == 1) {
AddContextMenuItem("Selected: Group Info", CMNU_GROUP_INFO);
}
if(gs.n + gs.constraints == 1 && gs.stylables == 1) {
AddContextMenuItem("Selected: Style Info", CMNU_STYLE_INFO);
}
if(gs.withEndpoints > 0) {
AddContextMenuItem("Selected: Select Edge Chain",
CMNU_SELECT_CHAIN);
if(c) {
AddContextMenuItem("Delete Point-Coincident Constraint",
CMNU_DEL_COINCIDENT);
}
}
AddContextMenuItem(NULL, CONTEXT_SEPARATOR);
if(LockedInWorkplane()) {
AddContextMenuItem("Cut Selection", CMNU_CUT_SEL);
AddContextMenuItem("Copy Selection", CMNU_COPY_SEL);
AddContextMenuItem("Cut", CMNU_CUT_SEL);
AddContextMenuItem("Copy", CMNU_COPY_SEL);
}
}
if(SS.clipboard.r.n > 0 && LockedInWorkplane()) {
AddContextMenuItem("Paste Selection", CMNU_PASTE_SEL);
AddContextMenuItem("Paste", CMNU_PASTE_SEL);
}
if(!selEmpty) {
AddContextMenuItem("Delete Selection", CMNU_DELETE_SEL);
if(itemsSelected) {
AddContextMenuItem("Delete", CMNU_DELETE_SEL);
AddContextMenuItem(NULL, CONTEXT_SEPARATOR);
AddContextMenuItem("Unselect All", CMNU_UNSELECT_ALL);
}
// If only one item is selected, then it must be the one that we just
// selected from the hovered item; in which case unselect all and hovered
// are equivalent.
if(!hover.IsEmpty() && selection.n > 1) {
AddContextMenuItem("Unselect Hovered", CMNU_UNSELECT_HOVERED);
}
int ret = ShowContextMenu();
if(ret != 0 && ret != CMNU_DEL_COINCIDENT && selEmpty) {
ToggleSelectionStateOf(&hover);
}
switch(ret) {
case CMNU_UNSELECT_ALL:
MenuEdit(MNU_UNSELECT_ALL);
break;
case CMNU_UNSELECT_HOVERED:
if(!hover.IsEmpty()) {
MakeUnselected(&hover, true);
}
break;
case CMNU_SELECT_CHAIN:
MenuEdit(MNU_SELECT_CHAIN);
break;
@ -645,8 +619,8 @@ void GraphicsWindow::MouseRightUp(double x, double y) {
case CMNU_DEL_COINCIDENT: {
SS.UndoRemember();
if(!hover.entity.v) break;
Entity *p = SK.GetEntity(hover.entity);
if(!gs.point[0].v) break;
Entity *p = SK.GetEntity(gs.point[0]);
if(!p->IsPoint()) break;
SK.constraint.ClearTags();
@ -658,6 +632,7 @@ void GraphicsWindow::MouseRightUp(double x, double y) {
}
}
SK.constraint.RemoveTagged();
ClearSelection();
break;
}
@ -667,7 +642,6 @@ void GraphicsWindow::MouseRightUp(double x, double y) {
case CMNU_GROUP_INFO: {
hGroup hg;
GroupSelection();
if(gs.entities == 1) {
hg = SK.GetEntity(gs.entity[0])->group;
} else if(gs.points == 1) {
@ -687,7 +661,6 @@ void GraphicsWindow::MouseRightUp(double x, double y) {
case CMNU_STYLE_INFO: {
hStyle hs;
GroupSelection();
if(gs.entities == 1) {
hs = Style::ForEntity(gs.entity[0]);
} else if(gs.points == 1) {
@ -726,8 +699,8 @@ void GraphicsWindow::MouseRightUp(double x, double y) {
break;
}
done:
context.active = false;
SS.later.showTW = true;
}
hRequest GraphicsWindow::AddRequest(int type) {
@ -1066,8 +1039,9 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) {
case 0:
default:
ClearPending();
if(hover.entity.v || hover.constraint.v) {
ToggleSelectionStateOf(&hover);
if(!hover.IsEmpty()) {
hoverWasSelectedOnMousedown = IsSelected(&hover);
MakeSelected(&hover);
}
break;
}
@ -1078,6 +1052,7 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) {
void GraphicsWindow::MouseLeftUp(double mx, double my) {
orig.mouseDown = false;
hoverWasSelectedOnMousedown = false;
switch(pending.operation) {
case DRAGGING_POINTS:

21
ui.h
View File

@ -456,6 +456,7 @@ public:
bool HasEndpoints(void);
};
Selection hover;
bool hoverWasSelectedOnMousedown;
List<Selection> selection;
void HitTestMakeSelection(Point2d mp);
void ClearSelection(void);
@ -486,18 +487,22 @@ public:
int n;
} gs;
void GroupSelection(void);
bool EntityIsSelected(hEntity he);
void ToggleSelectionStateOf(hEntity he, bool batch=false);
void ToggleSelectionStateOf(Selection *s, bool batch=false);
bool IsSelected(Selection *s);
bool IsSelected(hEntity he);
void MakeSelected(hEntity he);
void MakeSelected(Selection *s);
void MakeUnselected(hEntity he, bool coincidentPointTrick);
void MakeUnselected(Selection *s, bool coincidentPointTrick);
void SelectByMarquee(void);
void ClearSuper(void);
static const int CMNU_UNSELECT_ALL = 0x100;
static const int CMNU_CUT_SEL = 0x101;
static const int CMNU_COPY_SEL = 0x102;
static const int CMNU_PASTE_SEL = 0x103;
static const int CMNU_DELETE_SEL = 0x104;
static const int CMNU_SELECT_CHAIN = 0x105;
static const int CMNU_UNSELECT_HOVERED = 0x101;
static const int CMNU_CUT_SEL = 0x102;
static const int CMNU_COPY_SEL = 0x103;
static const int CMNU_PASTE_SEL = 0x104;
static const int CMNU_DELETE_SEL = 0x105;
static const int CMNU_SELECT_CHAIN = 0x106;
static const int CMNU_NEW_CUSTOM_STYLE = 0x110;
static const int CMNU_NO_STYLE = 0x111;
static const int CMNU_GROUP_INFO = 0x120;

View File

@ -1,6 +1,5 @@
show and modify view parameters (translate, rotate, scale)
context menu to hide / suppress groups by entity?
-----
associative entities from solid model, as a special group