Commit 5456d0f3 authored by twanvl's avatar twanvl

Movement of objects with the arrow keys in symbol editor

parent 2593d447
......@@ -23,8 +23,9 @@ String action_name_for(const set<SymbolPartP>& parts, const String& action) {
// ----------------------------------------------------------------------------- : Moving symbol parts
SymbolPartMoveAction::SymbolPartMoveAction(const set<SymbolPartP>& parts)
SymbolPartMoveAction::SymbolPartMoveAction(const set<SymbolPartP>& parts, const Vector2D& delta)
: parts(parts)
, delta(delta), moved(-delta)
, min_pos(Vector2D::infinity()), max_pos(-Vector2D::infinity())
, constrain(false)
, snap(0)
......@@ -419,3 +420,57 @@ void ReorderSymbolPartsAction::perform(bool to_undo) {
assert(part_id2 < symbol.parts.size());
swap(symbol.parts[part_id1], symbol.parts[part_id2]);
}
// ----------------------------------------------------------------------------- : Group symbol parts
GroupSymbolPartsActionBase::GroupSymbolPartsActionBase(Symbol& symbol)
: symbol(symbol)
{}
void GroupSymbolPartsActionBase::perform(bool to_undo) {
swap(symbol.parts, old_part_list);
}
GroupSymbolPartsAction::GroupSymbolPartsAction(Symbol& symbol, const set<SymbolPartP>& parts)
: GroupSymbolPartsActionBase(symbol)
{
// group parts in the old parts list
bool done = false;
SymbolGroupP group(new SymbolGroup);
group->name = _("Group");
FOR_EACH(p, symbol.parts) {
if (parts.find(p) != parts.end()) {
group->parts.push_back(p);
if (!done) {
done = true;
old_part_list.push_back(group);
}
} else {
// not affected
old_part_list.push_back(p);
}
}
}
String GroupSymbolPartsAction::getName(bool to_undo) const {
return _ACTION_("group parts");
}
UngroupSymbolPartsAction::UngroupSymbolPartsAction(Symbol& symbol, const set<SymbolPartP>& parts)
: GroupSymbolPartsActionBase(symbol)
{
// break up the parts in the old parts list
FOR_EACH(p, symbol.parts) {
if (parts.find(p) != parts.end() && p->isSymbolGroup()) {
// break up the group
SymbolGroup* g = p->isSymbolGroup();
FOR_EACH(p, g->parts) {
old_part_list.push_back(p);
}
} else {
// not affected
old_part_list.push_back(p);
}
}
}
String UngroupSymbolPartsAction::getName(bool to_undo) const {
return _ACTION_("ungroup parts");
}
......@@ -31,7 +31,7 @@ class SymbolPartListAction : public SymbolPartAction {};
/// Move some symbol parts
class SymbolPartMoveAction : public SymbolPartAction {
public:
SymbolPartMoveAction(const set<SymbolPartP>& parts);
SymbolPartMoveAction(const set<SymbolPartP>& parts, const Vector2D& delta = Vector2D());
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
......@@ -235,7 +235,38 @@ class ReorderSymbolPartsAction : public SymbolPartListAction {
private:
Symbol& symbol; ///< Symbol to swap the parts in
public:
size_t part_id1, part_id2; ///< Indeces of parts to swap
size_t part_id1, part_id2; ///< Indices of parts to swap
};
// ----------------------------------------------------------------------------- : Group symbol parts
/// Group multiple symbol parts together
class GroupSymbolPartsActionBase : public SymbolPartListAction {
public:
GroupSymbolPartsActionBase(Symbol& symbol);
virtual void perform(bool to_undo);
protected:
Symbol& symbol; ///< Symbol to group stuff in
vector<SymbolPartP> old_part_list; ///< Old part list of the symbol
};
/// Group multiple symbol parts together
class GroupSymbolPartsAction : public GroupSymbolPartsActionBase {
public:
GroupSymbolPartsAction(Symbol& symbol, const set<SymbolPartP>& parts);
virtual String getName(bool to_undo) const;
};
/// Break up one or more SymbolGroups
class UngroupSymbolPartsAction : public GroupSymbolPartsActionBase {
public:
UngroupSymbolPartsAction(Symbol& symbol, const set<SymbolPartP>& groups);
virtual String getName(bool to_undo) const;
};
......
......@@ -11,6 +11,7 @@
#include <gfx/bezier.hpp>
DECLARE_TYPEOF_COLLECTION(ControlPointP);
DECLARE_TYPEOF_COLLECTION(SymbolPartP);
// ----------------------------------------------------------------------------- : ControlPoint
......@@ -108,6 +109,7 @@ SymbolPartP read_new<SymbolPart>(Reader& reader) {
reader.handle(_("type"), type);
if (type == _("shape") || type.empty()) return new_intrusive<SymbolShape>();
else if (type == _("symmetry")) return new_intrusive<SymbolSymmetry>();
else if (type == _("group")) return new_intrusive<SymbolGroup>();
else {
throw ParseError(_("Unsupported symbol part type: '") + type + _("'"));
}
......@@ -218,6 +220,26 @@ IMPLEMENT_REFLECTION(SymbolSymmetry) {
}
}
// ----------------------------------------------------------------------------- : SymbolGroup
String SymbolGroup::typeName() const {
return _("group");
}
SymbolPartP SymbolGroup::clone() const {
SymbolGroupP part(new SymbolGroup(*this));
// also clone the parts inside
FOR_EACH(p, part->parts) {
p = p->clone();
}
return part;
}
IMPLEMENT_REFLECTION(SymbolGroup) {
REFLECT_BASE(SymbolPart);
REFLECT(parts);
}
// ----------------------------------------------------------------------------- : Symbol
IMPLEMENT_REFLECTION(Symbol) {
......
......@@ -18,6 +18,7 @@ DECLARE_POINTER_TYPE(ControlPoint);
DECLARE_POINTER_TYPE(SymbolPart);
DECLARE_POINTER_TYPE(SymbolShape);
DECLARE_POINTER_TYPE(SymbolSymmetry);
DECLARE_POINTER_TYPE(SymbolGroup);
DECLARE_POINTER_TYPE(Symbol);
// ----------------------------------------------------------------------------- : ControlPoint
......@@ -119,13 +120,16 @@ class SymbolPart : public IntrusivePtrVirtualBase {
/// Icon for this part
virtual int icon() const = 0;
/// Convert tot SymbolShape?
/// Convert to SymbolShape?
virtual SymbolShape* isSymbolShape() { return nullptr; }
virtual const SymbolShape* isSymbolShape() const { return nullptr; }
/// Convert tot SymbolSymmetry?
/// Convert to SymbolSymmetry?
virtual SymbolSymmetry* isSymbolSymmetry() { return nullptr; }
virtual const SymbolSymmetry* isSymbolSymmetry() const { return nullptr; }
/// Convert to SymbolGroup?
virtual SymbolGroup* isSymbolGroup() { return nullptr; }
virtual const SymbolGroup* isSymbolGroup() const { return nullptr; }
DECLARE_REFLECTION_VIRTUAL();
};
......@@ -184,7 +188,6 @@ class SymbolShape : public SymbolPart {
DECLARE_REFLECTION();
};
// ----------------------------------------------------------------------------- : SymbolSymmetry
enum SymbolSymmetryType
......@@ -213,12 +216,28 @@ class SymbolSymmetry : public SymbolPart {
DECLARE_REFLECTION();
};
// ----------------------------------------------------------------------------- : SymbolGroup
/// A group of symbol parts
class SymbolGroup : public SymbolPart {
public:
vector<SymbolPartP> parts; ///< The parts in this group
virtual String typeName() const;
virtual SymbolPartP clone() const;
virtual int icon() const { return SYMMETRY_REFLECTION + 1; }
virtual SymbolGroup* isSymbolGroup() { return this; }
virtual const SymbolGroup* isSymbolGroup() const { return this; }
DECLARE_REFLECTION();
};
// ----------------------------------------------------------------------------- : Symbol
/// An editable symbol, consists of any number of SymbolParts
class Symbol : public IntrusivePtrBase<Symbol> {
public:
/// The parts of this symbol
/// The parts of this symbol, first item is on top
vector<SymbolPartP> parts;
/// Actions performed on this symbol and the parts in it
ActionStack actions;
......
......@@ -352,7 +352,33 @@ void SymbolPointEditor::onChar(wxKeyEvent& ev) {
if (ev.GetKeyCode() == WXK_DELETE) {
deleteSelection();
} else {
ev.Skip();
// move selection using arrow keys
double step = 1.0 / settings.symbol_grid_size;
Vector2D delta;
if (ev.GetKeyCode() == WXK_LEFT) delta = Vector2D(-step, 0);
else if (ev.GetKeyCode() == WXK_RIGHT) delta = Vector2D( step, 0);
else if (ev.GetKeyCode() == WXK_UP) delta = Vector2D(0, -step);
else if (ev.GetKeyCode() == WXK_DOWN) delta = Vector2D(0, step);
else {
ev.Skip();
return;
}
// what to move
if (selection == SELECTED_POINTS || selection == SELECTED_LINE) {
// Move all selected points
controlPointMoveAction = new ControlPointMoveAction(selected_points);
getSymbol()->actions.add(controlPointMoveAction);
controlPointMoveAction->move(delta);
new_point += delta;
control.Refresh(false);
} else if (selection == SELECTED_HANDLE) {
// Move the selected handle
handleMoveAction = new HandleMoveAction(selected_handle);
getSymbol()->actions.add(handleMoveAction);
handleMoveAction->move(delta);
control.Refresh(false);
}
resetActions();
}
}
......
......@@ -387,7 +387,18 @@ void SymbolSelectEditor::onChar(wxKeyEvent& ev) {
resetActions();
control.Refresh(false);
} else {
ev.Skip();
// move selection using arrow keys
double step = 1.0 / settings.symbol_grid_size;
Vector2D delta;
if (ev.GetKeyCode() == WXK_LEFT) delta = Vector2D(-step, 0);
else if (ev.GetKeyCode() == WXK_RIGHT) delta = Vector2D( step, 0);
else if (ev.GetKeyCode() == WXK_UP) delta = Vector2D(0, -step);
else if (ev.GetKeyCode() == WXK_DOWN) delta = Vector2D(0, step);
else {
ev.Skip();
return;
}
getSymbol()->actions.add(new SymbolPartMoveAction(control.selected_parts, delta));
}
}
......
......@@ -74,41 +74,7 @@ void SymbolViewer::draw(DC& dc) {
}
// Draw all parts, in reverse order (bottom to top)
FOR_EACH_REVERSE(p, symbol->parts) {
if (SymbolShape* s = p->isSymbolShape()) {
if (s->combine == SYMBOL_COMBINE_OVERLAP && buffersFilled) {
// We will be overlapping some previous parts, write them to the screen
combineBuffers(dc, borderDC.get(), interiorDC.get());
// Clear the buffers
buffersFilled = false;
paintedSomething = true;
wxSize s = dc.GetSize();
if (borderDC) {
borderDC->SetBrush(*wxBLACK_BRUSH);
borderDC->SetPen( *wxTRANSPARENT_PEN);
borderDC->DrawRectangle(0, 0, s.GetWidth(), s.GetHeight());
}
interiorDC->SetBrush(*wxBLACK_BRUSH);
interiorDC->DrawRectangle(0, 0, s.GetWidth(), s.GetHeight());
}
// Paint the part itself
if (!paintedSomething) {
// No need to buffer
if (!interiorDC) interiorDC = getTempDC(dc);
combineSymbolShape(*s, dc, *interiorDC, true, false);
buffersFilled = true;
} else {
if (!borderDC) borderDC = getTempDC(dc);
if (!interiorDC) interiorDC = getTempDC(dc);
// Draw this shape to the buffer
combineSymbolShape(*s, *borderDC, *interiorDC, false, false);
buffersFilled = true;
}
// Paint symmetric versions of this part
// TODO
} else {
// symmetry, already handled above
}
combineSymbolPart(dc, *p, paintedSomething, buffersFilled, borderDC, interiorDC);
}
// Output the final parts from the buffer
......@@ -116,12 +82,55 @@ void SymbolViewer::draw(DC& dc) {
combineBuffers(dc, borderDC.get(), interiorDC.get());
}
}
void SymbolViewer::combineSymbolPart(DC& dc, const SymbolPart& part, bool& paintedSomething, bool& buffersFilled, MemoryDCP& borderDC, MemoryDCP& interiorDC) {
if (const SymbolShape* s = part.isSymbolShape()) {
if (s->combine == SYMBOL_COMBINE_OVERLAP && buffersFilled) {
// We will be overlapping some previous parts, write them to the screen
combineBuffers(dc, borderDC.get(), interiorDC.get());
// Clear the buffers
buffersFilled = false;
paintedSomething = true;
wxSize s = dc.GetSize();
if (borderDC) {
borderDC->SetBrush(*wxBLACK_BRUSH);
borderDC->SetPen( *wxTRANSPARENT_PEN);
borderDC->DrawRectangle(0, 0, s.GetWidth(), s.GetHeight());
}
interiorDC->SetBrush(*wxBLACK_BRUSH);
interiorDC->DrawRectangle(0, 0, s.GetWidth(), s.GetHeight());
}
// Paint the part itself
if (!paintedSomething) {
// No need to buffer
if (!interiorDC) interiorDC = getTempDC(dc);
combineSymbolShape(*s, dc, *interiorDC, true, false);
buffersFilled = true;
} else {
if (!borderDC) borderDC = getTempDC(dc);
if (!interiorDC) interiorDC = getTempDC(dc);
// Draw this shape to the buffer
combineSymbolShape(*s, *borderDC, *interiorDC, false, false);
buffersFilled = true;
}
// Paint symmetric versions of this part
// TODO
} else if (const SymbolSymmetry* s = part.isSymbolSymmetry()) {
// symmetry, already handled above
} else if (const SymbolGroup* g = part.isSymbolGroup()) {
FOR_EACH_CONST(p, g->parts) {
combineSymbolPart(dc, *p, paintedSomething, buffersFilled, borderDC, interiorDC);
}
}
}
void SymbolViewer::highlightPart(DC& dc, const SymbolPart& part, HighlightStyle style) {
if (const SymbolShape* s = part.isSymbolShape()) {
highlightPart(dc, *s, style);
} else if (const SymbolSymmetry* s = part.isSymbolSymmetry()) {
highlightPart(dc, *s);
} else if (const SymbolGroup* g = part.isSymbolGroup()) {
highlightPart(dc, *g, style);
} else {
throw InternalError(_("Invalid symbol part type"));
}
......@@ -154,6 +163,11 @@ void SymbolViewer::highlightPart(DC& dc, const SymbolShape& shape, HighlightStyl
void SymbolViewer::highlightPart(DC& dc, const SymbolSymmetry& sym) {
// TODO
}
void SymbolViewer::highlightPart(DC& dc, const SymbolGroup& group, HighlightStyle style) {
FOR_EACH_CONST(part, group.parts) {
highlightPart(dc, *part, style);
}
}
void SymbolViewer::combineSymbolShape(const SymbolShape& shape, DC& border, DC& interior, bool directB, bool directI) {
......
......@@ -45,12 +45,18 @@ class SymbolViewer : public SymbolView {
void draw(DC& dc);
void highlightPart(DC& dc, const SymbolPart& part, HighlightStyle style);
void highlightPart(DC& dc, const SymbolShape& shap, HighlightStyle style);
void highlightPart(DC& dc, const SymbolShape& shape, HighlightStyle style);
void highlightPart(DC& dc, const SymbolSymmetry& sym);
void highlightPart(DC& dc, const SymbolGroup& group, HighlightStyle style);
void onAction(const Action&, bool) {}
private:
typedef shared_ptr<wxMemoryDC> MemoryDCP;
/// Combine a symbol part with the dc
void SymbolViewer::combineSymbolPart(DC& dc, const SymbolPart& part, bool& paintedSomething, bool& buffersFilled, MemoryDCP& borderDC, MemoryDCP& interiorDC);
/// Combines a symbol part with what is currently drawn, the border and interior are drawn separatly
/** directB/directI are true if the border/interior is the screen dc, false if it
* is a temporary 1 bit one
......
......@@ -126,6 +126,8 @@ enum ChildMenuID {
, ID_SYMBOL_COMBINE_BORDER = ID_SYMBOL_COMBINE + 5 //SYMBOL_COMBINE_BORDER
, ID_SYMBOL_COMBINE_MAX
, ID_EDIT_DUPLICATE // duplicating symbol parts
, ID_EDIT_GROUP
, ID_EDIT_UNGROUP
, ID_VIEW_GRID
, ID_VIEW_GRID_SNAP
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment