Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve extensibility of the DiagramWidget #83

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 103 additions & 4 deletions cmd/diagramdemo/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
)
Expand All @@ -29,6 +30,67 @@ func forceanim(diagramWidget *diagramwidget.DiagramWidget) {
}
}

// ModifiedDragNode illustrates how to extend a node. This particular example
// disables vertical movement and vertical resizing
type ModifiedDragNode struct {
diagramwidget.BaseDiagramNode
label *widget.Label
}

// NewModifiedDragNode returns an instance of an ExtendedNode
func NewModifiedDragNode(nodeID string, diagramWidget *diagramwidget.DiagramWidget) diagramwidget.DiagramNode {
newNode := &ModifiedDragNode{}
newNode.label = widget.NewLabel("Vertical Changes Not Alloweed")
diagramwidget.InitializeBaseDiagramNode(newNode, diagramWidget, newNode.label, nodeID)
newNode.Refresh()
return newNode
}

// Dragged passes the DragEvent to the diagram for processing after removing any Y value changes
func (en *ModifiedDragNode) Dragged(event *fyne.DragEvent) {
modifiedDelta := fyne.Delta{
DX: event.Dragged.DX,
DY: 0,
}
modifiedDragEvent := &fyne.DragEvent{
PointEvent: event.PointEvent,
Dragged: modifiedDelta,
}
en.GetDiagram().DiagramNodeDragged(&en.BaseDiagramNode, modifiedDragEvent)
}

// HandleDragged passes the HandleDragged event to the BasseDiagramNode after removing any Y value changes
func (en *ModifiedDragNode) HandleDragged(handle *diagramwidget.Handle, event *fyne.DragEvent) {
modifiedDelta := fyne.Delta{
DX: event.Dragged.DX,
DY: 0,
}
modifiedDragEvent := &fyne.DragEvent{
PointEvent: event.PointEvent,
Dragged: modifiedDelta,
}
en.BaseDiagramNode.HandleDragged(handle, modifiedDragEvent)
}

// ModifiedPadsNode removes the default rectangle pad on the perimeter and adds two point pads,
// one at each side of the node
type ModifiedPadsNode struct {
diagramwidget.BaseDiagramNode
label *widget.Label
}

// NewModifiedPadsNode creates and initializes a ModifiedPadsNode
func NewModifiedPadsNode(nodeID string, diagramWidget *diagramwidget.DiagramWidget) diagramwidget.DiagramNode {
newNode := &ModifiedPadsNode{}
newNode.label = widget.NewLabel("Connection pads on left and right")
diagramwidget.InitializeBaseDiagramNode(newNode, diagramWidget, newNode.label, nodeID)
// Get rid of the default pad on the perimeter
newNode.SetConnectionPad(nil, "default")
newNode.SetConnectionPad(diagramwidget.NewPointPad(newNode.GetProperties().PadStrokeWidth), "left")
newNode.SetConnectionPad(diagramwidget.NewPointPad(newNode.GetProperties().PadStrokeWidth), "right")
return newNode
}

func main() {
app := app.New()
w := app.NewWindow("Diagram Demo")
Expand All @@ -37,10 +99,19 @@ func main() {

diagramWidget := diagramwidget.NewDiagramWidget("Diagram1")

scrollContainer := container.NewScroll(diagramWidget)

go forceanim(diagramWidget)

background := canvas.NewCircle(color.RGBA{
R: 252,
G: 244,
B: 3,
A: 255,
})

background.Resize(fyne.NewSize(200, 200))

diagramWidget.SetBackground(background)

// Node 0
node0Label := widget.NewLabel("Node0")
node0 := diagramwidget.NewDiagramNode(diagramWidget, node0Label, "Node0")
Expand Down Expand Up @@ -91,9 +162,29 @@ func main() {
}), "Node4")
node4.Move(fyne.Position{X: 400, Y: 400})

node5 := diagramwidget.NewDiagramNode(diagramWidget, widget.NewLabel("Node5"), "Node5")
node5 := diagramwidget.NewDiagramNode(diagramWidget, widget.NewButton("Node5: Toggle Background Graphic", func() {
if diagramWidget.GetBackground() == nil {
diagramWidget.SetBackground(background)
} else {
diagramWidget.SetBackground(nil)
}
}), "Node5")
node5.Move(fyne.NewPos(600, 200))

node6 := NewModifiedDragNode("Node6", diagramWidget)
node6.Move(fyne.NewPos(500, 0))

node7 := NewModifiedPadsNode("Node7", diagramWidget)
node7Size := node7.Size()
halfPointPadSize := diagramwidget.PointPadSize / 2
node7Pads := node7.GetConnectionPads()
node7DefaultPadPosition := fyne.NewPos(-halfPointPadSize, node7Size.Height/2-halfPointPadSize)
node7Pads["left"].Move(node7DefaultPadPosition)
node7RightPadPosition := fyne.NewPos(node7Size.Width-halfPointPadSize, node7Size.Height/2-halfPointPadSize)
node7Pads["right"].Move(node7RightPadPosition)
node7.Move(fyne.NewPos(500, 50))
node7.Refresh()

// Link0
link0 := diagramwidget.NewDiagramLink(diagramWidget, "Link0")
link0.SetSourcePad(node0.GetEdgePad())
Expand Down Expand Up @@ -146,7 +237,15 @@ func main() {
link5.AddMidpointAnchoredText("linkName", "Link 5")
link5.AddTargetDecoration(diagramwidget.NewArrowhead())

w.SetContent(scrollContainer)
link6 := diagramwidget.NewDiagramLink(diagramWidget, "Link6")
link6.SetSourcePad(node6.GetEdgePad())
link6.SetTargetPad(node7.GetConnectionPads()["left"])

link7 := diagramwidget.NewDiagramLink(diagramWidget, "Link7")
link7.SetSourcePad(node6.GetEdgePad())
link7.SetTargetPad(node7.GetConnectionPads()["right"])

w.SetContent(diagramWidget)

w.Resize(fyne.NewSize(600, 400))
w.ShowAndRun()
Expand Down
95 changes: 87 additions & 8 deletions widget/diagramwidget/anchoredtext.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ import (
// move by the same amount
type AnchoredText struct {
widget.BaseWidget
link *BaseDiagramLink
diagramElement DiagramElement
ID string
offset r2.Vec2
referencePosition fyne.Position
displayedTextBinding binding.String
Expand All @@ -30,12 +31,15 @@ type AnchoredText struct {
// NewAnchoredText creates an textual annotation for a link. After it is created, one of the
// three Add<position>AnchoredText methods must be called on the link to actually associate the
// anchored text with the appropriate reference point on the link.
func NewAnchoredText(text string) *AnchoredText {
func NewAnchoredText(text string, id ...string) *AnchoredText {
at := &AnchoredText{
offset: r2.MakeVec2(0, 0),
ForegroundColor: theme.ForegroundColor(),
referencePosition: fyne.Position{X: 0, Y: 0},
}
if len(id) > 0 {
at.ID = id[0]
}
at.displayedTextBinding = binding.NewString()
at.displayedTextBinding.Set(text)
at.textEntry = widget.NewEntryWithData(at.displayedTextBinding)
Expand All @@ -59,16 +63,28 @@ func (at *AnchoredText) CreateRenderer() fyne.WidgetRenderer {

// DataChanged is the callback function for the displayedTextBinding.
func (at *AnchoredText) DataChanged() {
callback := at.diagramElement.GetDiagram().AnchoredTextChangedCallback
if callback != nil {
callback(at)
}
at.Refresh()
}

// Displace moves the anchored text relative to its reference position.
func (at *AnchoredText) Displace(delta fyne.Position) {
at.Move(at.Position().Add(delta))
callback := at.diagramElement.GetDiagram().AnchoredTextChangedCallback
if callback != nil {
callback(at)
}
}

// DragEnd is one of the required methods for a draggable widget. It just refreshes the widget.
func (at *AnchoredText) DragEnd() {
callback := at.diagramElement.GetDiagram().AnchoredTextChangedCallback
if callback != nil {
callback(at)
}
at.Refresh()
}

Expand All @@ -77,14 +93,33 @@ func (at *AnchoredText) DragEnd() {
func (at *AnchoredText) Dragged(event *fyne.DragEvent) {
delta := fyne.Position{X: event.Dragged.DX, Y: event.Dragged.DY}
at.Move(at.Position().Add(delta))
callback := at.diagramElement.GetDiagram().AnchoredTextChangedCallback
if callback != nil {
callback(at)
}
at.Refresh()
}

// GetDiagramElement returns the diagram element to which the anchored text belongs
func (at *AnchoredText) GetDiagramElement() DiagramElement {
return at.diagramElement
}

// GetDisplayedTextBinding returns the binding for the displayed text
func (at *AnchoredText) GetDisplayedTextBinding() binding.String {
return at.displayedTextBinding
}

// GetOffset returns the X and Y values of the anchored text's offset from the reference position
func (at *AnchoredText) GetOffset() (X float64, Y float64) {
return at.offset.X, at.offset.Y
}

// GetReferencePosition returns the X and Y values of the anchored text's reference position
func (at *AnchoredText) GetReferencePosition() (X float64, Y float64) {
return float64(at.referencePosition.X), float64(at.referencePosition.Y)
}

// GetTextEntry returns the entry widget
func (at *AnchoredText) GetTextEntry() *widget.Entry {
return at.textEntry
Expand Down Expand Up @@ -114,8 +149,12 @@ func (at *AnchoredText) MouseOut() {
// and then calls the normal BaseWidget.Move method.
func (at *AnchoredText) Move(position fyne.Position) {
delta := r2.MakeVec2(float64(position.X-at.Position().X), float64(position.Y-at.Position().Y))
at.offset = at.offset.Add(delta)
at.BaseWidget.Move(position)
newOffset := at.offset.Add(delta)
at.SetOffsetNoCallback(newOffset.X, newOffset.Y)
callback := at.diagramElement.GetDiagram().AnchoredTextChangedCallback
if callback != nil {
callback(at)
}
}

// SetForegroundColor sets the text color
Expand All @@ -124,13 +163,53 @@ func (at *AnchoredText) SetForegroundColor(fc color.Color) {
at.Refresh()
}

// SetOffset sets the X and Y values of the anchored text's offset from the reference position
func (at *AnchoredText) SetOffset(X float64, Y float64) {
if at.offset.X != X || at.offset.Y != Y {
at.SetOffsetNoCallback(X, Y)
callback := at.diagramElement.GetDiagram().AnchoredTextChangedCallback
if callback != nil {
callback(at)
}
}
}

// SetOffsetNoCallback sets the X and Y values of the anchored text's offset from the reference position
func (at *AnchoredText) SetOffsetNoCallback(X float64, Y float64) {
if at.offset.X != X || at.offset.Y != Y {
at.offset.X = X
at.offset.Y = Y
delta := fyne.Delta{DX: float32(X), DY: float32(Y)}
at.BaseWidget.Move(at.referencePosition.Add(delta))
}
}

// SetReferencePosition sets the reference position of the anchored text and calls
// the BaseWidget.Move() method to actually move the displayed text
func (at *AnchoredText) SetReferencePosition(position fyne.Position) {
delta := fyne.Delta{DX: float32(position.X - at.referencePosition.X), DY: float32(position.Y - at.referencePosition.Y)}
// We don't want to change the offset here, so we call the BaseWidget.Move directly
at.BaseWidget.Move(at.Position().Add(delta))
at.referencePosition = position
if position != at.referencePosition {
at.SetReferencePositionNoCallback(position)
callback := at.diagramElement.GetDiagram().AnchoredTextChangedCallback
if callback != nil {
callback(at)
}
}
}

// SetReferencePositionNoCallback sets the reference position of the anchored text and calls
// the BaseWidget.Move() method to actually move the displayed text
func (at *AnchoredText) SetReferencePositionNoCallback(position fyne.Position) {
if position != at.referencePosition {
delta := fyne.Delta{DX: float32(position.X - at.referencePosition.X), DY: float32(position.Y - at.referencePosition.Y)}
// We don't want to change the offset here, so we call the BaseWidget.Move directly
at.BaseWidget.Move(at.Position().Add(delta))
at.referencePosition = position
}
}

// SetText sets the anchored text without invoking the callback
func (at *AnchoredText) SetText(text string) {
at.displayedTextBinding.Set(text)
}

// anchoredTextRenderer
Expand Down
Loading