1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
|
use ui;
use core:geometry;
/**
* An element on a slide.
*/
class Element on Render {
// Current position. Generally set by the layout.
private Rect myPos;
// Animations.
private Animation[] myAni;
// Visible at the last repaint?
private Bool visibleLastRepaint;
// Get/set the position.
Rect pos() : final { myPos; }
assign pos(Rect p) { myPos = p; }
// Get the minimum size of this element.
Size minSize() : abstract;
// Initialize any animations in here.
void setupAnimations(Presentation p) {
for (a in animations) {
a.setup(p, this);
}
}
// Setup repaint notifications.
void setupRepaint(Fn<void> callback) {}
// Cleanup anything that needs to be cleaned up after the presentation is closed.
void cleanup() {}
// Quick check if this element is interactive.
InteractiveElement? interactive() {
null;
}
// Extract the animations from this element. Expected to be called once on slide setup, so might
// be somewhat slow.
Animation[] animations() { myAni; }
// Add an animation.
void animation(Animation a) {
myAni << a;
}
// Draw, considering any animations in here.
void draw(Graphics g, Nat step, Duration time) {
if (before(g, step, time))
draw(g);
after(g, step, time);
}
// Draw this element at the location indicated by `pos`.
void draw(Graphics g) : abstract;
// Call if you need to re-implement 'draw'.
protected Bool before(Graphics g, Nat step, Duration time) : final {
Bool show = true;
Nat count = myAni.count;
for (Nat i = 0; i < count; i++) {
show &= myAni[i].before(this, g, step, time);
}
visibleLastRepaint = show;
show;
}
protected void after(Graphics g, Nat step, Duration time) : final {
for (Nat i = myAni.count; i > 0; i--) {
myAni[i - 1].after(this, g, step, time);
}
}
// Was this item visible at the last repaint?
// Used mainly for hit-tests for mouse coordinates.
Bool visible() : final {
visibleLastRepaint;
}
}
/**
* Element that the user can interact with.
*/
class InteractiveElement extends Element {
// Called when the cursor is above the element. Return true if a repaint is needed.
Bool mouseMoved(Point pt) { false; }
// Called when the mouse was clicked above the element. Return true if a repaint is needed.
Bool mouseClicked(Point pt, Bool down, MouseButton button) { false; }
// Called when the mouse was double-clicked above the element. Return true if a repaint is needed.
Bool mouseDblClicked(Point pt, MouseButton button) { false; }
// Quick check if the element is interactive.
InteractiveElement? interactive() : override {
this;
}
}
/**
* Connect elements to the layout system.
*/
package class ElementWrap extends layout:Component {
Element control;
init(Element e) {
init() { control = e; }
}
Rect pos() { control.pos; }
assign pos(Rect p) { control.pos = p; }
Size minSize() { control.minSize; }
}
// Tell the layout how to wrap things.
ElementWrap component(Element e) {
ElementWrap(e);
}
|