Eventcatcher Example: Simple Canvas

As a learning exercise for <$eventcatcher/> I thought I’d see if I had all the pieces I’d need to build something like a note canvas. It turns out you can build something basic with very little code, and I think serves as a good example to learn from. I always learn best from extremely minimalist examples, so this is just me sharing what I’ve learned back to the forum.

There are now many good tools for structured note layouts like Mohammad 's Pinboard and BurningTreeC 's Muuri. Eric’s recent PasteUp tool, is quite comprehensive now and very flexible.

Anyways, below is a quick animation of what I mean by “note canvas” - a little bit emulating “sticky notes” on a large canvas that you can drag and drop to adjust around. The attached file you can just drag and drop into TiddlyWiki.com to demo for yourself.

Animation

Canvas.json (17.8 KB)

Tiddlers:

  1. Canvas: That’s the file shown - for this demo, any tiddler with a tag of “Note” will appear
  2. Canvas/Macros: Procedures to capture and store the mouse and click events
  3. Canvas/Styles: Styling for the canvas and notes (sizes, colors, etc. )
  4. Note: Literally just an example with a wikitext table to illustrate it’s just tiddler content
  5. Picture: Logo just to illustrate images work the same way

I haven’t decided yet if I’m going to build anything with this yet. It’d be a lot fo work and out of laziness I’d probably use Eric’s PasteUp and/or the new TW5-Graph from Flibbles which I love, but again thought it was interesting that this was so relatively easy to start and requires no plugins.

10 Likes

Excellent work @stobot !

As part of a rewrite for $eventcatcher for v5.4.0, the code you have shared could be simplified even further to avoid the need for a tiddler that tells you whether dragging is active. I am working on a small set of implementation examples to extend the documentation for the eventcatcher widget to cover common use cases, and this would fit in nicely.

5 Likes

An amazing piece of work! Thank you for sharing, @stobot.
It’s eye-catching—you did a great job!

I’m thinking about how your code could be used with TW 5.4 to create a Volantis page layout: a layout to give your tiddlers wings :wink:
@saqimtiaz, you might have some ideas here!

Ref:

1 Like

I’m very impressed! I didn’t imagine it would be so simple to do. Moreover, it’s quite easy to see how one could extend this to resizable cards and adjustable z-indexes. It only takes a little imagination to see how to handle multiple canvases. It would be a little more work to allow a note to be used on multiple canvases, but even there, the path is fairly clear.

Thank you very much for sharing!

3 Likes

Not to detract in any way from the work presented here, but for those curious about this the floats rewrite I posted not long ago is exactly that, an opinionated attempt at extending these basics to also support z-index and resizing.

Part of the goal of that exploration - apart from testing a rewrite of eventcatcher - is to explore possibilites for new primitives for the core that could replace or supplement the existing popup/alert/notification/modal sub-systems.

1 Like

I remember being impressed with that too.

I have no current need for either of them, or for @EricShulman’s offering, at the moment. But it’s heartening to know they’re available if and when.

1 Like

Thanks for the positive feedback, I’m excited for the further development of <$eventcatcher>! I worked on this largely because you (@saqimtiaz) pointed me in this direction a long time ago and it seemed like a feature that should get more “press”. Thanks for that link @Mohammad - super impressive!

Because I still had some time to tinker with the above a bit, I added just a little more so I could actually use it for something today. I’ll share the updated code here, and add a few illustrations of implementing this (as you can tell I’m very visual).

canvas 2025-11-17.json (18.7 KB)

First, addressing somewhat of a bug fix - When dragging a note across another note, you lose things when the dragged item is not “on top”, it now automatically sets “on top / z-index” when you start dragging.

overlap

Secondly, I refactored each canvas to setup a specific tiddler to hold all of the individual canvas settings (rather than write back to the notes themselves). This allows you to to have as many canvases with unique settings as you want with the same notes involved. This is the tiddler parameter, and then I added a little delete button in the bottom left if you want to get rid of a canvas / start over.

tiddler

Then I added 3 more parameters for overwriting a few other defaults. The first is canvas-style so you can just add some inline css to style on the fly - stored in the canvas tiddler. Colors, size (height is especially useful, default is 30vh)

canvas-style

The second is note-style, useful again for sizes, colors, alignment, etc.

note-style

And lastly, note-template. The default is normal tiddler content (<$transclude mode='block'/>) but here I show grabbing just a field (title). Note that currentTiddler is set to the tiddler, so you can use stuff like {{!!title}}

note-template

Anyways, aside from the bugfix and the storage of canvas settings in an independent tiddler, the rest is a bit “hacky”, but that’s kind of my style - function & flexibility over beauty.

9 Likes

Hi @stobot

I really like this piece of work. Thanks for sharing.

A couple of comments:

  1. I imported the original version into an empty version of 5.3.8 and it worked a treat, but when I imported the 2025-11-17 version the it stops working.
  2. I would be really good if clicking somewhere on the note allows it to be edited inline or opens the associated tiddler for editing.

Keep up the good work!

Cheers, Rob

Hi @Rob_Jopling - I just tested on empty and it’s working fine for me, but in case it wasn’t clear, you can’t have them both loaded. The second one was changed structurally quite a bit and so they’re not compatible together.

Good point on the ability to edit, attached is a tweak where you can click on a note to open it, and ctrl-click on it to open in edit mode. I also changed the cursor to the point to make that more clear.

canvas 2025-11-18.json (19.0 KB)

Can this be made into a page template so that we can load the canvas layout as needed and the filter for the tiddlers to be displayed shall be dynamically changed using edit-text search box at the top

Brilliant @stobot

Thanks for that. And the latest version works fine.

Cheers, Rob

Still just an experiment / learning exercise, but added sizing and rotating by holding down a key while doing the dragging left and right, added a legend that shows when hovering over the canvas to show you the keys.

There are some issues - doesn’t like it when you go too fast, and if you drag outside of the canvas, it locks you in that mode until you click again. Not dealbreakers, but worth noting, relatively easy fixes.

arunnbabu81 - sounds possible, but outside the scope of what I’m playing with at the moment, I’d love to see you or someone pursue something like that though!

canvas-2025-11-19.json (21.3 KB)

Animation

8 Likes

Wow! Just wow.

(And still so clearly simple!)

1 Like

man, this is cool, i could see a lot of possibilities with this.
any chance of implementing transparency?

Really enjoying your work on this @stobot !

I tried quickly porting this to use the new $eventcatcher (part of the floats demo and expected to be a part of the TW v5.4.0 release) as it would benefit from the pointer capture support, alleviating problems areas like dragging too fast. I ran into an unexpected issue, currently the notes are completely redrawn when their position changes, due to the wrapping $let widget that declares a note-style variable, which releases the pointer capture.

Instead, I recommend directly assigning the style properties to the note div using the style.property syntax as follows, I have also updated the syntax to use the pointerCapture support in the new eventcatcher.:

\procedure canvas(tiddler:blank,filter:"[tag[Note]!is[draft]]",canvas-style:"",note-style:"",note-template:"<$transclude tiddler=<<currentTiddler>> mode='block'/>")
<$let width-default=200 height-default=200>
<div class="canvas" style=<<canvas-style>>>
<$list filter=<<filter>>>
<$eventcatcher selector=".canvas-note" $pointerdown="<<note-drag-start>>" $pointermove="<<note-drag-move>>" $pointerup="<<note-drag-end>>" pointerCapture="dynamic" $click="<<canvas-edit>>">
<div class={{{ [<tiddler>get[note]match<currentTiddler>then[canvas-note canvas-note-active]else[canvas-note]] }}}
  style.top=`${ [<currentTiddler>addsuffix[.y]] :map[<tiddler>getindex<currentTiddler>] }$px`
  style.left=`${ [<currentTiddler>addsuffix[.x]] :map[<tiddler>getindex<currentTiddler>] }$px` 
  style.width=`${ [<currentTiddler>addsuffix[.width]] :map[<tiddler>getindex<currentTiddler>else<width-default>] }$px` 
  style.rotate=`${ [<currentTiddler>addsuffix[.rotate]] :map[<tiddler>getindex<currentTiddler>else[0]] }$deg;$(note-style)$`
  style.height=`${ [<currentTiddler>addsuffix[.height]] :map[<tiddler>getindex<currentTiddler>else<height-default>] }$px`
>
<<note-template>>
</div>
</$eventcatcher>
</$list>
<div class="canvas-delete">
<%if [<tiddler>is[tiddler]] %>
<$button actions="<<canvas-delete>>" class="tc-btn-invisible">{{$:/core/images/delete-button}}</$button>
<%endif%>
</div>
<div class="canvas-help">
Clicking: Normal=Open, Ctrl=Edit | Dragging: Normal=Move, Shift=Size, Alt=Rotate
</div>
</div>
</$let>
\end

It would be especially helpful in terms of testing the pointer capture support if you wanted to play around with that upcoming version of eventcatcher.

2 Likes

Hi @Justin_H - in general I left the note-style property open so you could use any valid css. As an example, I think you’re talking about making the note background transparent? If so, here’s a screenshot of doing that using background-color:hsl(0 0 0 / 0). I also set the canvas to white to improve the contrast a bit and then gave it light border.

<<canvas canvas-style:"background-color:white;border:1px solid gainsboro;" note-style:"background-color:hsl(0 0 0 / 0);">>

Animation

If you meant applying transparency via dragging like move / size / rotate then look in the stobot/canvas/macros tiddler and you’ll see a pattern that you can adapt. I can give more detailed direction there if that’s what you intended, but I suspect not.

3 Likes

That’s awesome @saqimtiaz - I’ll definitely play around with it and report back. I didn’t know about the style.property syntax, I love how much cleaner it makes it than what I was having to do. The one thing I’m not sure about there is how I’d be able to arbitrarily just paste on the end the parameter string of note-style - I see you added it on the end of rotate? Is that valid? I was having problems so haven’t been able to test.

For some reason, moving the style onto the div directly made $mouseup trigger on every drag which it doesn’t do with the let statement (on 5.3.8). That was really annoying, so I just moved the $let inside the $eventcatcher for now until I test the new widget (“5.4.0”) you put together, maybe it doesn’t have that side effect with the new version.

thats awesome!

I was actually thinking about svgs or pngs to use as markers on a map or arrows, kinda like a dnd map or draw.io, but a tables a cool idea too, like custom statblocks.

theres so many cool ideas for this :nerd_face:

That was just a typo resulting from editing directly in the standard TW editor, something I am not used to. You should be able to use both the style.property syntax attributes and the style attribute together, just assign them directly to the div and not to a variable first.

mouseup or click? mouseup being triggered excessively was what happened with the new $eventcatcher due to the DIV being re-rendered every time its styles were updated. I can’t replicate that in v5.3.8. I do however get click being triggered at the end of every drag.

I would suggest avoiding the click event and replacing all mouse events with pointer equivalents. Then in pointerup check to see if a drag or resize has occurred and if it hasn’t, trigger the click actions.

Alternatively, use the matchSelector attribute of $eventcatcher to only trigger the resize/move actions when clicking a border/padding area around the note, and let the interior of the note receive click events.

This moves TiddlyWiki up a level.

I am thinking of docking to the left or right, top or bottom, so dragging an item and hitting the left border makes it occupy the left 50% of the container (canvas)!

1 Like