mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-15 14:16:07 -06:00
RichEdit: Diagram
This commit is contained in:
parent
987582e88a
commit
a8c3be8dc6
11 changed files with 358 additions and 276 deletions
|
|
@ -311,27 +311,27 @@ void ToolButton::LeftDown(Point, dword)
|
|||
{
|
||||
Refresh();
|
||||
if(repeat)
|
||||
WhenAction();
|
||||
Action();
|
||||
}
|
||||
|
||||
void ToolButton::LeftRepeat(Point, dword)
|
||||
{
|
||||
Refresh();
|
||||
if(repeat)
|
||||
WhenAction();
|
||||
Action();
|
||||
}
|
||||
|
||||
void ToolButton::LeftUp(Point, dword)
|
||||
{
|
||||
Refresh();
|
||||
if(!repeat)
|
||||
WhenAction();
|
||||
Action();
|
||||
}
|
||||
|
||||
bool ToolButton::HotKey(dword key)
|
||||
{
|
||||
if(key == accel) {
|
||||
WhenAction();
|
||||
Action();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -55,22 +55,26 @@ void DiagramEditor::TheBar(Bar& bar)
|
|||
bar.Add(line_dash, DPI(50));
|
||||
bar.Add(ink);
|
||||
bar.Add(paper);
|
||||
bar.Gap();
|
||||
bar.Separator();
|
||||
Size isz = IconSz();
|
||||
DiagramItem m;
|
||||
m.pt[0] = Point(2, 2);
|
||||
m.pt[1] = Point(isz.cx - 2, isz.cy - 2);
|
||||
m.width = ~line_width;
|
||||
m.dash = ~line_dash;
|
||||
m.shape = ~shape;
|
||||
m.ink = ~ink;
|
||||
m.paper = ~paper;
|
||||
m.cap[0] = ~line_start;
|
||||
m.cap[1] = ~line_end;
|
||||
bar.Add(MakeIcon(m, isz), [=]{
|
||||
for(int i = 0; i < tool_count; i++) {
|
||||
DiagramItem m = tl[i];
|
||||
m.pt[0] = Point(2, 2);
|
||||
m.pt[1] = Point(isz.cx - 2, isz.cy - 2);
|
||||
m.width = log(m.width + 1);
|
||||
bar.Add(MakeIcon(m, isz), [=] {
|
||||
CancelSelection();
|
||||
if(tool == i)
|
||||
tool = -1;
|
||||
else {
|
||||
tool = i;
|
||||
GetAttrs(tl[i]);
|
||||
}
|
||||
SetBar();
|
||||
// SetAttrs();
|
||||
});
|
||||
})
|
||||
.Key(get_i(i, K_1, K_2, K_3, K_4))
|
||||
.Check(tool == i);
|
||||
}
|
||||
bar.Break();
|
||||
// ink.DarkContent(IsDarkContent());
|
||||
text_editor.FontTools(bar);
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
#include "RichEdit.h"
|
||||
|
||||
namespace Upp {
|
||||
|
||||
void DiagramEditor::SetCursor(int i)
|
||||
{
|
||||
cursor = i;
|
||||
if(i < 0)
|
||||
return;
|
||||
sel.FindAdd(i);
|
||||
GetAttrs();
|
||||
}
|
||||
|
||||
DiagramItem& DiagramEditor::CursorItem()
|
||||
{
|
||||
static DiagramItem nil;
|
||||
if(cursor >= 0 && cursor < data.item.GetCount())
|
||||
return data.item[cursor];
|
||||
return nil;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -96,6 +96,9 @@ DiagramEditor::DiagramEditor()
|
|||
for(int i = 0; i < 10; i++)
|
||||
line_width.Add(i);
|
||||
line_width << [=] { SetAttrs(ATTR_WIDTH); };
|
||||
|
||||
tl[0].shape = DiagramItem::SHAPE_LINE;
|
||||
tl[1].shape = DiagramItem::SHAPE_ROUNDRECT;
|
||||
|
||||
ResetUndo();
|
||||
Sync();
|
||||
|
|
@ -112,22 +115,20 @@ Image DiagramEditor::MakeIcon(DiagramItem& m, Size isz)
|
|||
{
|
||||
struct IconMaker : ImageMaker {
|
||||
Size isz;
|
||||
DiagramItem& m;
|
||||
DiagramItem m;
|
||||
String Key() const override {
|
||||
return StoreAsString(m) + String((byte *)&isz, sizeof(isz));
|
||||
return StoreAsString(const_cast<DiagramItem&>(m)) + String((byte *)&isz, sizeof(isz));
|
||||
}
|
||||
Image Make() const override {
|
||||
ImagePainter iw(isz);
|
||||
iw.Scale(DPI(1));
|
||||
iw.Clear();
|
||||
m.Paint(iw);
|
||||
return iw;
|
||||
}
|
||||
|
||||
IconMaker(DiagramItem& m) : m(m) {}
|
||||
};
|
||||
|
||||
IconMaker mk(m);
|
||||
IconMaker mk;
|
||||
mk.m = m;
|
||||
mk.isz = isz;
|
||||
return MakeImage(mk);
|
||||
}
|
||||
|
|
@ -181,8 +182,10 @@ void DiagramEditor::Paint(Draw& w)
|
|||
Size dsz = data.GetSize();
|
||||
iw.Move(dsz.cx, 0).Line(dsz.cx, dsz.cy).Line(0, dsz.cy).Stroke(0.2, SColorHighlight());
|
||||
|
||||
if(data.item.GetCount() == 0)
|
||||
iw.DrawText(DPI(30), DPI(30), "Right-click to insert item(s)", ArialZ(30).Italic(), SLtGray());
|
||||
if(data.item.GetCount() == 0) {
|
||||
iw.DrawText(DPI(30), DPI(30), "Right-click to insert item(s)", ArialZ(10).Italic(), SLtGray());
|
||||
iw.DrawText(DPI(30), DPI(50), "Double-click to edit text", ArialZ(10).Italic(), SLtGray());
|
||||
}
|
||||
|
||||
if(display_grid)
|
||||
for(int x = 0; x < dsz.cx; x += 8)
|
||||
|
|
@ -313,4 +316,22 @@ void DiagramEditor::Reset()
|
|||
Sync();
|
||||
}
|
||||
|
||||
bool DiagramEditor::Key(dword key, int count)
|
||||
{
|
||||
switch(key) {
|
||||
case K_ESCAPE:
|
||||
if(tool >= 0) {
|
||||
tool = -1;
|
||||
SetBar();
|
||||
return true;
|
||||
}
|
||||
if(IsCursor()) {
|
||||
KillCursor();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return Ctrl::Key(key, count);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -46,6 +46,7 @@ public:
|
|||
void RightUp(Point p, dword keyflags) override;
|
||||
void HorzMouseWheel(Point p, int zdelta, dword keyflags) override;
|
||||
void MouseWheel(Point p, int zdelta, dword keyflags) override;
|
||||
bool Key(dword key, int count) override;
|
||||
void Layout() override;
|
||||
|
||||
private:
|
||||
|
|
@ -68,16 +69,21 @@ private:
|
|||
int pi; // point index
|
||||
};
|
||||
|
||||
bool moving = false; // moving hysteresis
|
||||
|
||||
Vector<Cn> conns; // connections, created at the drag start, updates line connections
|
||||
|
||||
BinUndoRedo undoredo;
|
||||
|
||||
int tool = 0;
|
||||
ToolBar toolbar;
|
||||
DropList shape, line_start, line_end, line_width, line_dash;
|
||||
DiaRichEdit text_editor;
|
||||
|
||||
ColorButton ink, paper;
|
||||
|
||||
int tool = -1;
|
||||
int tool_count = 2; // TODO
|
||||
DiagramItem tl[2];
|
||||
|
||||
ScrollBars sb;
|
||||
|
||||
|
|
@ -114,6 +120,8 @@ private:
|
|||
Image DashIcon(int i);
|
||||
void PrepareConns();
|
||||
void UseConns();
|
||||
void Grid(int shape, Point& p);
|
||||
void Grid(const DiagramItem& m, Point& p) { Grid(m.shape, p); }
|
||||
|
||||
|
||||
void FixPositions();
|
||||
|
|
@ -127,9 +135,13 @@ private:
|
|||
ATTR_PAPER = 0x0040,
|
||||
ATTR_ALL = 0xffffffff
|
||||
};
|
||||
void SetAttrs(DiagramItem& m, dword attrs);
|
||||
void SetAttrs(dword attr);
|
||||
void GetAttrs(const DiagramItem& m);
|
||||
void GetAttrs();
|
||||
|
||||
DiagramItem& AddItem(int shape);
|
||||
|
||||
void Copy();
|
||||
void Cut();
|
||||
void Paste();
|
||||
|
|
|
|||
|
|
@ -92,12 +92,19 @@ Image DiagramEditor::CursorImage(Point p, dword keyflags)
|
|||
|
||||
void DiagramEditor::LeftDouble(Point p, dword keyflags)
|
||||
{
|
||||
if(IsCursor())
|
||||
if(IsCursor() && !(keyflags & K_CTRL))
|
||||
StartText();
|
||||
}
|
||||
|
||||
void DiagramEditor::Grid(int shape, Point& p)
|
||||
{
|
||||
p = shape == DiagramItem::SHAPE_LINE ? p / 8 * 8 : p / 16 * 16;
|
||||
}
|
||||
|
||||
void DiagramEditor::LeftDown(Point p, dword keyflags)
|
||||
{
|
||||
moving = false;
|
||||
|
||||
conns.Clear();
|
||||
|
||||
Map(p);
|
||||
|
|
@ -106,6 +113,17 @@ void DiagramEditor::LeftDown(Point p, dword keyflags)
|
|||
dragstart = dragcurrent = p;
|
||||
|
||||
SetCapture();
|
||||
|
||||
if(tool >= 0) {
|
||||
KillCursor();
|
||||
DiagramItem& m = AddItem(tl[tool].shape);
|
||||
m = tl[tool];
|
||||
Grid(m, p);
|
||||
m.pt[0] = m.pt[1] = p;
|
||||
m.FixPosition();
|
||||
draghandle = Point(1, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if(IsCursor()) {
|
||||
Point h = GetHandle(cursor, p);
|
||||
|
|
@ -124,14 +142,24 @@ void DiagramEditor::LeftDown(Point p, dword keyflags)
|
|||
|
||||
int i = FindItem(p);
|
||||
if(i >= 0) {
|
||||
SetCursor(i);
|
||||
dragfrom = GetCursorRect();
|
||||
if(dragfrom.Contains(p)) {
|
||||
sdragfrom.SetCount(sel.GetCount());
|
||||
for(int i = 0; i < sel.GetCount(); i++)
|
||||
sdragfrom[i] = data.item[sel[i]];
|
||||
PrepareConns();
|
||||
draghandle = Null;
|
||||
if((keyflags & K_CTRL) && sel.Find(i) >= 0) {
|
||||
sel.RemoveKey(i);
|
||||
if(sel.GetCount())
|
||||
SetCursor(sel.Top());
|
||||
else
|
||||
KillCursor();
|
||||
}
|
||||
else
|
||||
SetCursor(i);
|
||||
if(IsCursor()) {
|
||||
dragfrom = GetCursorRect();
|
||||
if(dragfrom.Contains(p)) {
|
||||
sdragfrom.SetCount(sel.GetCount());
|
||||
for(int i = 0; i < sel.GetCount(); i++)
|
||||
sdragfrom[i] = data.item[sel[i]];
|
||||
PrepareConns();
|
||||
draghandle = Null;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
@ -163,10 +191,10 @@ void DiagramEditor::MouseMove(Point p, dword keyflags)
|
|||
Sync();
|
||||
return;
|
||||
}
|
||||
if(HasCapture() && IsCursor()) {
|
||||
if(HasCapture() && IsCursor() && (moving || Distance(dragstart, p) >= 8)) {
|
||||
moving = true;
|
||||
DiagramItem& m = CursorItem();
|
||||
if(grid)
|
||||
p = m.IsLine() ? p / 8 * 8 : p / 16 * 16;
|
||||
Grid(m, p);
|
||||
if(IsNull(draghandle)) { // move selection
|
||||
Rectf to = dragfrom.Offseted(p - dragstart);
|
||||
Pointf tp = to.TopLeft();
|
||||
|
|
@ -199,23 +227,16 @@ void DiagramEditor::MouseMove(Point p, dword keyflags)
|
|||
}
|
||||
}
|
||||
|
||||
void DiagramEditor::LeftUp(Point p, dword keyflags)
|
||||
void DiagramEditor::LeftUp(Point, dword)
|
||||
{
|
||||
conns.Clear();
|
||||
|
||||
Map(p);
|
||||
|
||||
Sync();
|
||||
doselection = false;
|
||||
moving = doselection = false;
|
||||
Commit();
|
||||
// if(Distance(dragstart, p) < 2 && CursorItem().IsTextClick(p))
|
||||
// StartText();
|
||||
}
|
||||
|
||||
void DiagramEditor::RightDown(Point p, dword keyflags)
|
||||
{
|
||||
LeftDown(p, keyflags);
|
||||
|
||||
Map(p);
|
||||
|
||||
auto PopPaint = [=](Draw& w, const Image& m, bool sel) {
|
||||
|
|
@ -228,10 +249,12 @@ void DiagramEditor::RightDown(Point p, dword keyflags)
|
|||
};
|
||||
|
||||
FinishText();
|
||||
|
||||
if(IsCursor()) {
|
||||
DiagramItem& m = CursorItem();
|
||||
|
||||
int ii = FindItem(p);
|
||||
if(ii >= 0) {
|
||||
DiagramItem& m = data.item[ii];
|
||||
if(m.IsLine()) {
|
||||
SetCursor(ii);
|
||||
Point h = GetHandle(cursor, p);
|
||||
if(h.x) {
|
||||
int i = h.x > 0;
|
||||
|
|
@ -284,9 +307,8 @@ void DiagramEditor::RightDown(Point p, dword keyflags)
|
|||
|
||||
CancelSelection();
|
||||
|
||||
if(grid)
|
||||
p = p / 16 * 16;
|
||||
Pointf cp = Null;
|
||||
Grid(si, p);
|
||||
Pointf cp = Null; // connect line with nearest connection point
|
||||
if(si == DiagramItem::SHAPE_LINE) {
|
||||
double mind = DBL_MAX;
|
||||
for(const DiagramItem& m : data.item)
|
||||
|
|
@ -299,17 +321,10 @@ void DiagramEditor::RightDown(Point p, dword keyflags)
|
|||
}
|
||||
}
|
||||
|
||||
int i = data.item.GetCount();
|
||||
if(si == 0) { // insert lines before shapes
|
||||
i = 0;
|
||||
while(i < data.item.GetCount() && data.item[i].IsLine())
|
||||
i++;
|
||||
data.item.Insert(i);
|
||||
}
|
||||
DiagramItem& m = data.item.At(i);
|
||||
DiagramItem& m = AddItem(si);
|
||||
if(IsNull(cp)) {
|
||||
m.pt[0] = Pointf(p) - Pointf(64, 32);
|
||||
m.pt[1] = Pointf(p) + Pointf(64, 32);
|
||||
m.pt[0] = p;
|
||||
m.pt[1] = p + Point(128, 64);
|
||||
}
|
||||
else {
|
||||
m.pt[0] = cp;
|
||||
|
|
@ -317,7 +332,6 @@ void DiagramEditor::RightDown(Point p, dword keyflags)
|
|||
}
|
||||
SetAttrs(ATTR_ALL);
|
||||
m.shape = si;
|
||||
SetCursor(i);
|
||||
Sync();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,32 +2,69 @@
|
|||
|
||||
namespace Upp {
|
||||
|
||||
void DiagramEditor::SetCursor(int i)
|
||||
{
|
||||
cursor = i;
|
||||
if(i < 0) {
|
||||
sel.Clear();
|
||||
return;
|
||||
}
|
||||
sel.FindAdd(i);
|
||||
GetAttrs();
|
||||
}
|
||||
|
||||
DiagramItem& DiagramEditor::CursorItem()
|
||||
{
|
||||
static DiagramItem nil;
|
||||
if(cursor >= 0 && cursor < data.item.GetCount())
|
||||
return data.item[cursor];
|
||||
return nil;
|
||||
}
|
||||
|
||||
DiagramItem& DiagramEditor::AddItem(int shape)
|
||||
{
|
||||
int i = data.item.GetCount();
|
||||
if(shape == 0) { // insert lines before shapes
|
||||
i = 0;
|
||||
while(i < data.item.GetCount() && data.item[i].IsLine())
|
||||
i++;
|
||||
data.item.Insert(i);
|
||||
}
|
||||
DiagramItem& m = data.item.At(i);
|
||||
SetCursor(i);
|
||||
return m;
|
||||
}
|
||||
|
||||
void DiagramEditor::SetAttrs(DiagramItem& m, dword attrs)
|
||||
{
|
||||
if(attrs & ATTR_SHAPE)
|
||||
m.shape = ~shape;
|
||||
if(attrs & ATTR_CAP0)
|
||||
m.cap[0] = ~line_start;
|
||||
if(attrs & ATTR_CAP1)
|
||||
m.cap[1] = ~line_end;
|
||||
if(attrs & ATTR_WIDTH)
|
||||
m.width = ~line_width;
|
||||
if(attrs & ATTR_DASH)
|
||||
m.dash = ~line_dash;
|
||||
if(attrs & ATTR_INK)
|
||||
m.ink = ~ink;
|
||||
if(attrs & ATTR_PAPER)
|
||||
m.paper = ~paper;
|
||||
}
|
||||
|
||||
void DiagramEditor::SetAttrs(dword attrs)
|
||||
{
|
||||
for(int i = 0; i < sel.GetCount(); i++) {
|
||||
DiagramItem& m = data.item[sel[i]];
|
||||
if(attrs & ATTR_SHAPE)
|
||||
m.shape = ~shape;
|
||||
if(attrs & ATTR_CAP0)
|
||||
m.cap[0] = ~line_start;
|
||||
if(attrs & ATTR_CAP1)
|
||||
m.cap[1] = ~line_end;
|
||||
if(attrs & ATTR_WIDTH)
|
||||
m.width = ~line_width;
|
||||
if(attrs & ATTR_DASH)
|
||||
m.dash = ~line_dash;
|
||||
if(attrs & ATTR_INK)
|
||||
m.ink = ~ink;
|
||||
if(attrs & ATTR_PAPER)
|
||||
m.paper = ~paper;
|
||||
}
|
||||
for(int i = 0; i < sel.GetCount(); i++)
|
||||
SetAttrs(data.item[sel[i]], attrs);
|
||||
if(tool >= 0)
|
||||
SetAttrs(tl[tool], attrs);
|
||||
Sync();
|
||||
Commit();
|
||||
}
|
||||
|
||||
void DiagramEditor::GetAttrs()
|
||||
void DiagramEditor::GetAttrs(const DiagramItem& m)
|
||||
{
|
||||
DiagramItem& m = CursorItem();
|
||||
shape <<= m.shape;
|
||||
line_start <<= m.cap[0];
|
||||
line_end <<= m.cap[1];
|
||||
|
|
@ -37,6 +74,12 @@ void DiagramEditor::GetAttrs()
|
|||
paper <<= m.paper;
|
||||
}
|
||||
|
||||
|
||||
void DiagramEditor::GetAttrs()
|
||||
{
|
||||
GetAttrs(CursorItem());
|
||||
}
|
||||
|
||||
void DiagramEditor::MoveFrontBack(bool back)
|
||||
{
|
||||
FinishText();
|
||||
|
|
|
|||
|
|
@ -31,10 +31,9 @@ file
|
|||
DiagramEditor.h,
|
||||
DiagramEditor.cpp,
|
||||
DiagramMouse.cpp,
|
||||
DiagramCursor.cpp,
|
||||
DiagramOps.cpp,
|
||||
ColumnPopUp.cpp,
|
||||
Bar.cpp,
|
||||
DiagramBar.cpp,
|
||||
Text.cpp,
|
||||
DiagramClip.cpp,
|
||||
Diagram.iml,
|
||||
|
|
|
|||
|
|
@ -56,31 +56,12 @@ void DiagramItem::FixPosition()
|
|||
pt[0].y -= y;
|
||||
pt[1].y -= y;
|
||||
}
|
||||
/* Sizef sz = GetRect().GetSize();
|
||||
if(max(sz.cx, sz.cy) < 8) {
|
||||
auto Fix = [](double& a1, double& a2) {
|
||||
int d = a2 - a1;
|
||||
if(abs(d) < 8)
|
||||
a2 = a1 + sgn(d) * 8;
|
||||
};
|
||||
Fix(pt[0].x, pt[1].x);
|
||||
Fix(pt[0].y, pt[1].y);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
Vector<Pointf> DiagramItem::GetConnections() const
|
||||
{
|
||||
Vector<Pointf> p;
|
||||
if(IsLine()) {
|
||||
p << pt[0] << pt[1];
|
||||
return p;
|
||||
}
|
||||
Rectf r = GetRect();
|
||||
p << r.TopCenter() << r.BottomCenter();
|
||||
if(shape != SHAPE_PARALLELOGRAM)
|
||||
p << r.CenterLeft() << r.CenterRight();
|
||||
return p;
|
||||
if(IsLine())
|
||||
return;
|
||||
if(pt[1].x - pt[0].x < 8)
|
||||
pt[1].x = pt[0].x + 8;
|
||||
if(pt[1].y - pt[0].y < 8)
|
||||
pt[1].y = pt[0].y + 8;
|
||||
}
|
||||
|
||||
bool DiagramItem::IsClick(Point p) const
|
||||
|
|
@ -147,149 +128,6 @@ Rect DiagramItem::GetTextEditRect() const
|
|||
return GetRect();
|
||||
}
|
||||
|
||||
void DiagramItem::Paint(Painter& w, dword style, const Index<Pointf> *conn) const
|
||||
{
|
||||
Zoom zoom = Diagram::TextZoom();
|
||||
|
||||
RichText txt = ParseQTF(qtf);
|
||||
|
||||
static Vector<double> dashes[5] = { { 0 }, { 1, 1 }, { 2 }, { 1, 2 }, { 1, 2 } };
|
||||
|
||||
auto DoDash = [&] {
|
||||
if(dash) {
|
||||
Vector<double> d = clone(dashes[clamp(dash, 0, __countof(dashes))]);
|
||||
for(double& h : d)
|
||||
h *= width;
|
||||
w.Dash(d, 0);
|
||||
}
|
||||
};
|
||||
|
||||
RGBA sel1 = 150 * SColorHighlight();
|
||||
RGBA sel2 = 150 * Gray();
|
||||
|
||||
if(IsLine()) {
|
||||
Pointf v = pt[1] - pt[0];
|
||||
if(style) {
|
||||
w.Move(pt[0]).Line(pt[1]).EndPath();
|
||||
w.Begin();
|
||||
if((style & EDITOR) && width == 0)
|
||||
w.Dash("5 1").Stroke(1, 100 * sel2);
|
||||
if(style & (Display::CURSOR | Display::SELECT)) {
|
||||
w.LineCap(LINECAP_ROUND).Stroke(width + 12, (style & Display::SELECT ? 30 : 200) * sel2);
|
||||
double r = (width + 12) / 2 - 1;
|
||||
w.Circle(pt[0], r).Fill(sel1);
|
||||
w.Circle(pt[1], r).Fill(sel1);
|
||||
}
|
||||
w.End();
|
||||
}
|
||||
double d = Length(v);
|
||||
v = Upp::Normalize(v);
|
||||
|
||||
Pointf a1 = pt[0];
|
||||
Pointf a2 = pt[1];
|
||||
if(d > 4 * width) { // enough length to have caps
|
||||
if(cap[0] == CAP_ARROW)
|
||||
a1 += v * 4 * width;
|
||||
if(cap[1] == CAP_ARROW)
|
||||
a2 -= v * 4 * width;
|
||||
}
|
||||
|
||||
w.Move(a1).Line(a2);
|
||||
DoDash();
|
||||
w.Stroke(width, ink);
|
||||
|
||||
Pointf o = Orthogonal(v);
|
||||
if(d > 4 * width) {
|
||||
auto PaintCap = [&](int k, Pointf p, Pointf a) {
|
||||
Pointf oo = max(3.0, width * 2) * o;
|
||||
switch(k) {
|
||||
case CAP_NONE:
|
||||
w.Circle(p, width / 2).Fill(ink);
|
||||
break;
|
||||
case CAP_ARROW:
|
||||
w.Move(p).Line(a + oo).Line(a - oo).Fill(ink);
|
||||
break;
|
||||
case CAP_CIRCLE:
|
||||
w.Circle(p, 5).Fill(ink);
|
||||
break;
|
||||
}
|
||||
};
|
||||
PaintCap(cap[0], pt[0], a1 + v);
|
||||
PaintCap(cap[1], pt[1], a2 - v);
|
||||
}
|
||||
|
||||
int cx = Distance(pt[0], pt[1]);
|
||||
int txt_cy = txt.GetHeight(zoom, cx);
|
||||
|
||||
w.Begin();
|
||||
double angle = Bearing(pt[1] - pt[0]);
|
||||
if(angle >= -M_PI / 2 && angle <= M_PI / 2) {
|
||||
w.Translate(pt[0] - o * (txt_cy + 10));
|
||||
w.Rotate(angle);
|
||||
}
|
||||
else {
|
||||
w.Translate(pt[1] + o * (txt_cy + 10));
|
||||
w.Rotate(angle + M_PI);
|
||||
}
|
||||
txt.Paint(zoom, w, 0, 0, cx);
|
||||
w.End();
|
||||
}
|
||||
else {
|
||||
if(style & (Display::CURSOR | Display::SELECT)) {
|
||||
w.RoundedRectangle(GetRect(), 5)
|
||||
.Fill((style & Display::SELECT ? 30 : 200) * sel2);
|
||||
w.RoundedRectangle(GetRect().Inflated(2), 5)
|
||||
.Stroke(6, (style & Display::SELECT ? 30 : 200) * sel1);
|
||||
}
|
||||
|
||||
int txt_cy = txt.GetHeight(zoom, GetRect().GetWidth());
|
||||
Rectf r(pt[0], pt[1]);
|
||||
r.Normalize();
|
||||
r.Deflate(width / 2);
|
||||
Pointf c = r.CenterPoint();
|
||||
int sz = min(r.Width(), r.Height());
|
||||
switch(shape) {
|
||||
case SHAPE_ROUNDRECT:
|
||||
w.RoundedRectangle(r.left, r.top, r.GetWidth(), r.GetHeight(), sz > 30 ? 8 : sz > 15 ? 4 : 2);
|
||||
break;
|
||||
case SHAPE_OVAL:
|
||||
if(r.GetWidth() > r.GetHeight()) {
|
||||
double ra = r.GetHeight() / 2;
|
||||
w.Move(r.left + ra, r.top)
|
||||
.Line(r.right - ra, r.top)
|
||||
.Arc(r.right - ra, r.top + ra, ra, -M_PI / 2, M_PI)
|
||||
.Line(r.left + ra, r.bottom)
|
||||
.Arc(r.left + ra, r.top + ra, ra, M_PI / 2, M_PI);
|
||||
break;
|
||||
}
|
||||
case SHAPE_ELLIPSE:
|
||||
w.Ellipse(r);
|
||||
break;
|
||||
case SHAPE_DIAMOND:
|
||||
w.Move(c.x, r.top).Line(r.right, c.y).Line(c.x, r.bottom).Line(r.left, c.y).Close();
|
||||
break;
|
||||
case SHAPE_PARALLELOGRAM:
|
||||
w.Move(r.left + r.Width() / 6, r.top).Line(r.right, r.top)
|
||||
.Line(r.right - r.Width() / 6, r.bottom).Line(r.left, r.bottom).Close();
|
||||
break;
|
||||
default:
|
||||
w.Rectangle(r);
|
||||
break;
|
||||
}
|
||||
DoDash();
|
||||
w.Fill(paper).Stroke(width, ink);
|
||||
txt.Paint(zoom, w, r.left, r.top + (r.GetHeight() - txt_cy) / 2, r.GetWidth());
|
||||
|
||||
if(style & GRID)
|
||||
for(Pointf p : GetConnections()) {
|
||||
w.Circle(p, 5);
|
||||
if(conn && conn->Find(p) >= 0)
|
||||
w.Fill(128 * SYellow());
|
||||
w.Stroke(1, 190 * SColorHighlight());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DiagramItem::Save(StringBuffer& r) const
|
||||
{
|
||||
r << Shape[clamp(shape, 0, Shape.GetCount() - 1)] << ' ';
|
||||
|
|
|
|||
172
uppsrc/RichText/DiagramShape.cpp
Normal file
172
uppsrc/RichText/DiagramShape.cpp
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
#include "RichText.h"
|
||||
|
||||
namespace Upp {
|
||||
|
||||
Vector<Pointf> DiagramItem::GetConnections() const
|
||||
{
|
||||
Vector<Pointf> p;
|
||||
if(IsLine()) {
|
||||
p << pt[0] << pt[1];
|
||||
return p;
|
||||
}
|
||||
Rectf r = GetRect();
|
||||
p << r.TopCenter() << r.BottomCenter();
|
||||
if(shape != SHAPE_PARALLELOGRAM)
|
||||
p << r.CenterLeft() << r.CenterRight();
|
||||
return p;
|
||||
}
|
||||
|
||||
void DiagramItem::Paint(Painter& w, dword style, const Index<Pointf> *conn) const
|
||||
{
|
||||
Zoom zoom = Diagram::TextZoom();
|
||||
|
||||
RichText txt = ParseQTF(qtf);
|
||||
|
||||
static Vector<double> dashes[5] = { { 0 }, { 1, 1 }, { 2 }, { 1, 2 }, { 1, 2 } };
|
||||
|
||||
auto DoDash = [&] {
|
||||
if(dash) {
|
||||
Vector<double> d = clone(dashes[clamp(dash, 0, __countof(dashes))]);
|
||||
for(double& h : d)
|
||||
h *= width;
|
||||
w.Dash(d, 0);
|
||||
}
|
||||
};
|
||||
|
||||
RGBA sel1 = 150 * SColorHighlight();
|
||||
RGBA sel2 = 150 * Gray();
|
||||
|
||||
auto Stroke = [&] {
|
||||
if(width)
|
||||
w.Stroke(width, ink);
|
||||
else
|
||||
if(style & GRID)
|
||||
w.Stroke(0.2, sel1);
|
||||
};
|
||||
|
||||
if(IsLine()) {
|
||||
Pointf v = pt[1] - pt[0];
|
||||
if(style) {
|
||||
w.Move(pt[0]).Line(pt[1]).EndPath();
|
||||
w.Begin();
|
||||
if((style & EDITOR) && width == 0)
|
||||
w.Dash("5 1").Stroke(1, 100 * sel2);
|
||||
if(style & (Display::CURSOR | Display::SELECT)) {
|
||||
w.LineCap(LINECAP_ROUND).Stroke(width + 12, (style & Display::SELECT ? 30 : 200) * sel2);
|
||||
double r = (width + 12) / 2 - 1;
|
||||
w.Circle(pt[0], r).Fill(sel1);
|
||||
w.Circle(pt[1], r).Fill(sel1);
|
||||
}
|
||||
w.End();
|
||||
}
|
||||
double d = Length(v);
|
||||
v = Upp::Normalize(v);
|
||||
|
||||
Pointf a1 = pt[0];
|
||||
Pointf a2 = pt[1];
|
||||
if(d > 4 * width) { // enough length to have caps
|
||||
if(cap[0] == CAP_ARROW)
|
||||
a1 += v * 4 * width;
|
||||
if(cap[1] == CAP_ARROW)
|
||||
a2 -= v * 4 * width;
|
||||
}
|
||||
|
||||
w.Move(a1).Line(a2);
|
||||
DoDash();
|
||||
Stroke();
|
||||
|
||||
Pointf o = Orthogonal(v);
|
||||
if(d > 4 * width) {
|
||||
auto PaintCap = [&](int k, Pointf p, Pointf a) {
|
||||
Pointf oo = max(3.0, width * 2) * o;
|
||||
switch(k) {
|
||||
case CAP_NONE:
|
||||
w.Circle(p, width / 2).Fill(ink);
|
||||
break;
|
||||
case CAP_ARROW:
|
||||
w.Move(p).Line(a + oo).Line(a - oo).Fill(ink);
|
||||
break;
|
||||
case CAP_CIRCLE:
|
||||
w.Circle(p, 5).Fill(ink);
|
||||
break;
|
||||
}
|
||||
};
|
||||
PaintCap(cap[0], pt[0], a1 + v);
|
||||
PaintCap(cap[1], pt[1], a2 - v);
|
||||
}
|
||||
|
||||
int cx = Distance(pt[0], pt[1]);
|
||||
int txt_cy = txt.GetHeight(zoom, cx);
|
||||
|
||||
w.Begin();
|
||||
double angle = Bearing(pt[1] - pt[0]);
|
||||
if(angle >= -M_PI / 2 && angle <= M_PI / 2) {
|
||||
w.Translate(pt[0] - o * (txt_cy + 10));
|
||||
w.Rotate(angle);
|
||||
}
|
||||
else {
|
||||
w.Translate(pt[1] + o * (txt_cy + 10));
|
||||
w.Rotate(angle + M_PI);
|
||||
}
|
||||
txt.Paint(zoom, w, 0, 0, cx);
|
||||
w.End();
|
||||
}
|
||||
else {
|
||||
if(style & (Display::CURSOR | Display::SELECT)) {
|
||||
w.RoundedRectangle(GetRect(), 5)
|
||||
.Fill((style & Display::SELECT ? 30 : 200) * sel2);
|
||||
w.RoundedRectangle(GetRect().Inflated(2), 5)
|
||||
.Stroke(6, (style & Display::SELECT ? 30 : 200) * sel1);
|
||||
}
|
||||
|
||||
int txt_cy = txt.GetHeight(zoom, GetRect().GetWidth());
|
||||
Rectf r(pt[0], pt[1]);
|
||||
r.Normalize();
|
||||
r.Deflate(width / 2);
|
||||
Pointf c = r.CenterPoint();
|
||||
int sz = min(r.Width(), r.Height());
|
||||
switch(shape) {
|
||||
case SHAPE_ROUNDRECT:
|
||||
w.RoundedRectangle(r.left, r.top, r.GetWidth(), r.GetHeight(), sz > 30 ? 8 : sz > 15 ? 4 : 2);
|
||||
break;
|
||||
case SHAPE_OVAL:
|
||||
if(r.GetWidth() > r.GetHeight()) {
|
||||
double ra = r.GetHeight() / 2;
|
||||
w.Move(r.left + ra, r.top)
|
||||
.Line(r.right - ra, r.top)
|
||||
.Arc(r.right - ra, r.top + ra, ra, -M_PI / 2, M_PI)
|
||||
.Line(r.left + ra, r.bottom)
|
||||
.Arc(r.left + ra, r.top + ra, ra, M_PI / 2, M_PI);
|
||||
break;
|
||||
}
|
||||
case SHAPE_ELLIPSE:
|
||||
w.Ellipse(r);
|
||||
break;
|
||||
case SHAPE_DIAMOND:
|
||||
w.Move(c.x, r.top).Line(r.right, c.y).Line(c.x, r.bottom).Line(r.left, c.y).Close();
|
||||
break;
|
||||
case SHAPE_PARALLELOGRAM:
|
||||
w.Move(r.left + r.Width() / 6, r.top).Line(r.right, r.top)
|
||||
.Line(r.right - r.Width() / 6, r.bottom).Line(r.left, r.bottom).Close();
|
||||
break;
|
||||
default:
|
||||
w.Rectangle(r);
|
||||
break;
|
||||
}
|
||||
DoDash();
|
||||
w.Fill(paper);
|
||||
Stroke();
|
||||
|
||||
txt.Paint(zoom, w, r.left, r.top + (r.GetHeight() - txt_cy) / 2, r.GetWidth());
|
||||
|
||||
if(style & GRID)
|
||||
for(Pointf p : GetConnections()) {
|
||||
w.Circle(p, 5);
|
||||
if(conn && conn->Find(p) >= 0)
|
||||
w.Fill(128 * SYellow());
|
||||
w.Stroke(1, 190 * SColorHighlight());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -38,6 +38,7 @@ file
|
|||
Diagram readonly separator,
|
||||
Diagram.h,
|
||||
Diagram.cpp,
|
||||
DiagramShape.cpp,
|
||||
RichDiagram.cpp,
|
||||
Info readonly separator,
|
||||
srcdoc.tpp,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue