diff --git a/uppsrc/CtrlLib/ToolButton.cpp b/uppsrc/CtrlLib/ToolButton.cpp index cc6300b73..1405ba9f0 100644 --- a/uppsrc/CtrlLib/ToolButton.cpp +++ b/uppsrc/CtrlLib/ToolButton.cpp @@ -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; diff --git a/uppsrc/RichEdit/Bar.cpp b/uppsrc/RichEdit/DiagramBar.cpp similarity index 88% rename from uppsrc/RichEdit/Bar.cpp rename to uppsrc/RichEdit/DiagramBar.cpp index e37b71682..59f41e3e3 100644 --- a/uppsrc/RichEdit/Bar.cpp +++ b/uppsrc/RichEdit/DiagramBar.cpp @@ -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); diff --git a/uppsrc/RichEdit/DiagramCursor.cpp b/uppsrc/RichEdit/DiagramCursor.cpp deleted file mode 100644 index cf1a739c0..000000000 --- a/uppsrc/RichEdit/DiagramCursor.cpp +++ /dev/null @@ -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; -} - -} \ No newline at end of file diff --git a/uppsrc/RichEdit/DiagramEditor.cpp b/uppsrc/RichEdit/DiagramEditor.cpp index 05f98b5d0..e7ba78405 100644 --- a/uppsrc/RichEdit/DiagramEditor.cpp +++ b/uppsrc/RichEdit/DiagramEditor.cpp @@ -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(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); +} + } \ No newline at end of file diff --git a/uppsrc/RichEdit/DiagramEditor.h b/uppsrc/RichEdit/DiagramEditor.h index e6e816b09..890ce6b90 100644 --- a/uppsrc/RichEdit/DiagramEditor.h +++ b/uppsrc/RichEdit/DiagramEditor.h @@ -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 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(); diff --git a/uppsrc/RichEdit/DiagramMouse.cpp b/uppsrc/RichEdit/DiagramMouse.cpp index 8318c2f5f..fd75722c4 100644 --- a/uppsrc/RichEdit/DiagramMouse.cpp +++ b/uppsrc/RichEdit/DiagramMouse.cpp @@ -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(); } diff --git a/uppsrc/RichEdit/DiagramOps.cpp b/uppsrc/RichEdit/DiagramOps.cpp index 75370258b..fec44d06c 100644 --- a/uppsrc/RichEdit/DiagramOps.cpp +++ b/uppsrc/RichEdit/DiagramOps.cpp @@ -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(); diff --git a/uppsrc/RichEdit/RichEdit.upp b/uppsrc/RichEdit/RichEdit.upp index 980e53325..2268c7344 100644 --- a/uppsrc/RichEdit/RichEdit.upp +++ b/uppsrc/RichEdit/RichEdit.upp @@ -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, diff --git a/uppsrc/RichText/Diagram.cpp b/uppsrc/RichText/Diagram.cpp index 3f432b9bd..0012f5adb 100644 --- a/uppsrc/RichText/Diagram.cpp +++ b/uppsrc/RichText/Diagram.cpp @@ -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 DiagramItem::GetConnections() const -{ - Vector 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 *conn) const -{ - Zoom zoom = Diagram::TextZoom(); - - RichText txt = ParseQTF(qtf); - - static Vector dashes[5] = { { 0 }, { 1, 1 }, { 2 }, { 1, 2 }, { 1, 2 } }; - - auto DoDash = [&] { - if(dash) { - Vector 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)] << ' '; diff --git a/uppsrc/RichText/DiagramShape.cpp b/uppsrc/RichText/DiagramShape.cpp new file mode 100644 index 000000000..23c7ae42f --- /dev/null +++ b/uppsrc/RichText/DiagramShape.cpp @@ -0,0 +1,172 @@ +#include "RichText.h" + +namespace Upp { + +Vector DiagramItem::GetConnections() const +{ + Vector 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 *conn) const +{ + Zoom zoom = Diagram::TextZoom(); + + RichText txt = ParseQTF(qtf); + + static Vector dashes[5] = { { 0 }, { 1, 1 }, { 2 }, { 1, 2 }, { 1, 2 } }; + + auto DoDash = [&] { + if(dash) { + Vector 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()); + } + } +} + +} \ No newline at end of file diff --git a/uppsrc/RichText/RichText.upp b/uppsrc/RichText/RichText.upp index a7d920ca1..b19f5e4be 100644 --- a/uppsrc/RichText/RichText.upp +++ b/uppsrc/RichText/RichText.upp @@ -38,6 +38,7 @@ file Diagram readonly separator, Diagram.h, Diagram.cpp, + DiagramShape.cpp, RichDiagram.cpp, Info readonly separator, srcdoc.tpp,