Skip to content

Commit 9bc8d41

Browse files
committed
Merge branch 'observable_from_getters'
* observable_from_getters: EBEAST: b/part-thumb.vue: freeze immutable allnotes, no need to observe EBEAST: utilities.js: resize_canvas: return the devicepixelratio EBEAST: b/piano-roll.vue: swap <style/> and <template/> sections EBEAST: b/piano-roll.vue: move to await and observable_from_getters() Use observable_from_getters() to track all data that requires `await` and use dom_update() to handle dependency tracking and reliable DOM canvas redraws. BSE: bsepart.cc: emit notify:last_tick, noteschanged, linkschanged events EBEAST: b/part-thumb.vue: move to observable_from_getters() EBEAST: vue_observable_from_getters(): gracefully handle missing getter EBEAST: b/track-view.vue: use dom_animate_playback() EBEAST: b/track-list.vue: use dom_animate_playback() EBEAST: utilities.js: add dom_animate_playback() to vue_mixins.dom_updates This allowes Vue components with the dom_updates mixin to enable/disable calls to dom_animate_playback (active) via dom_trigger_animate_playback(). EBEAST: vue_observable_from_getters: only call getter if !!predicate EBEAST: b/track-list.vue: use Vue.observable_from_getters() for Bse.Song EBEAST: utilities.js: re-add async-warning for dom_update EBEAST: b/track-list.vue: remove unused attr EBEAST: b/track-view.vue: use Vue.observable_from_getters() for Bse.Track EBEAST: provide Vue.observable_from_getters() for async getters with signals EBEAST: return `disconnect()` function from Bse.ObjectIface.on() EBEAST: b/track-view.vue: use explicit setup/cleanup code for async track API EBEAST: b/track-list.vue: properly handle await queries and cleanups EBEAST: utilities.js: add support for `priv_tmpl` to vue_mixins.data_tmpl EBEAST: utilities.js: add copy_recursively() for Arrays and simple Objects EBEAST: utilities.js: add equals_recursively() EBEAST: utilities.js: add discard_remote() stub EBEAST: utilities.js: track this.dom_update() calls reactively Since vuejs/vue#7573, Vue only tracks data dependencies during its VNode render() function which is unsuitable for drawing into DOM nodes (e.g. subsequent width/height patching by Vue will re-erase <canvas/> elements). The `dom_updates` Mixin now calls `this.dom_update()` for reliable rendering into DOM elements, *after* Vue has patched the DOM tree, and tracks dependencies during synchronous calls. Signed-off-by: Tim Janik <[email protected]>
2 parents 57f6a47 + 05ea0d7 commit 9bc8d41

9 files changed

+635
-349
lines changed

bse/bsepart.cc

+6-1
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,7 @@ part_update_last_tick (BsePart *self)
339339
self->last_tick_SL = last_tick;
340340
BSE_SEQUENCER_UNLOCK ();
341341
g_object_notify ((GObject*) self, "last-tick");
342+
self->as<Bse::PartImpl*>()->emit_event ("notify:last_tick");
342343
bse_part_links_changed (self);
343344
}
344345

@@ -357,7 +358,10 @@ range_changed_notify_handler (gpointer data)
357358
self->range_min_note = BSE_MAX_NOTE;
358359
self->range_max_note = 0;
359360
if (min_note <= max_note)
360-
g_signal_emit (self, signal_range_changed, 0, tick, duration, min_note, max_note);
361+
{
362+
g_signal_emit (self, signal_range_changed, 0, tick, duration, min_note, max_note);
363+
self->as<Bse::PartImpl*>()->emit_event ("noteschanged");
364+
}
361365
}
362366
handler_id_range_changed = 0;
363367

@@ -427,6 +431,7 @@ links_changed_notify_handler (gpointer data)
427431
BsePart *self = (BsePart*) sfi_ring_pop_head (&plist_links_changed);
428432
self->links_queued = FALSE;
429433
g_signal_emit (self, signal_links_changed, 0);
434+
self->as<Bse::PartImpl*>()->emit_event ("linkschanged");
430435
}
431436
handler_id_links_changed = 0;
432437

ebeast/app.html

+1
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@
100100
// utilities.js
101101
import * as Util from './utilities.js'; // depends on Vue
102102
window.Util = Util;
103+
Vue.prototype.observable_from_getters = Util.vue_observable_from_getters;
103104
for (let directivename in Util.vue_directives) // register all utility directives
104105
Vue.directive (directivename, Util.vue_directives[directivename]);
105106
loadstatus.innerText += "Loaded Util...\n";

ebeast/b/part-thumb.vue

+17-25
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,17 @@
3030

3131
<script>
3232
const tick_quant = 384; // FIXME
33+
34+
function observable_part_data () {
35+
const data = {
36+
partname: { getter: c => this.part.get_name(), notify: n => this.part.on ("notify:uname", n), },
37+
lasttick: { getter: c => this.part.get_last_tick(), notify: n => this.part.on ("notify:last_tick", n), },
38+
allnotes: { default: [], notify: n => this.part.on ("noteschanged", n),
39+
getter: async c => Object.freeze (await this.part.list_notes_crossing (0, CONFIG.MAXINT)), },
40+
};
41+
return this.observable_from_getters (data, () => this.part);
42+
}
43+
3344
module.exports = {
3445
name: 'b-part-thumb',
3546
mixins: [ Util.vue_mixins.dom_updates, Util.vue_mixins.hyphen_props ],
@@ -39,29 +50,10 @@ module.exports = {
3950
index: { type: Number, },
4051
trackindex: { type: Number, },
4152
},
42-
data_tmpl: { partname: "",
43-
allnotes: [],
44-
lasttick: 0,
45-
},
46-
watch: {
47-
part: { immediate: true, async handler (n, o) {
48-
// first send out async queries in parallel
49-
let partname = this.part.get_name();
50-
let lasttick = this.part.get_last_tick();
51-
let allnotes = this.part.list_notes_crossing (0, CONFIG.MAXINT);
52-
// then, await results in batch
53-
partname = await partname;
54-
lasttick = await lasttick;
55-
allnotes = await allnotes;
56-
// finally, assign Vue-reactive data without further suspension (await)
57-
this.partname = partname;
58-
this.lasttick = lasttick;
59-
this.allnotes = allnotes;
60-
} },
61-
},
53+
data() { return observable_part_data.call (this); },
6254
computed: {
63-
tickscale: function() { return 10 / 384.0; }, // FIXME
64-
pxoffset: function() { return this.tick * 10 / 384.0; }, // FIXME
55+
tickscale: function() { return 10 / 384.0; }, // FIXME
56+
pxoffset: function() { return this.tick * this.tickscale; }, // FIXME
6557
canvas_width: function() {
6658
return this.tickscale * Math.floor ((this.lasttick + tick_quant - 1) / tick_quant) * tick_quant;
6759
},
@@ -77,10 +69,10 @@ module.exports = {
7769
function render_canvas () {
7870
// canvas setup
7971
const canvas = this.$refs['canvas'];
80-
Util.resize_canvas (canvas, canvas.clientWidth, canvas.clientHeight, true);
72+
const pixelratio = Util.resize_canvas (canvas, canvas.clientWidth, canvas.clientHeight, true);
8173
const ctx = canvas.getContext ('2d'), cstyle = getComputedStyle (canvas), csp = cstyle.getPropertyValue.bind (cstyle);
8274
const width = canvas.width, height = canvas.height;
83-
const tickscale = this.tickscale * window.devicePixelRatio;
75+
const tickscale = this.tickscale * pixelratio;
8476
//const width = canvas.clientWidth, height = canvas.clientHeight;
8577
//canvas.width = width; canvas.height = height;
8678
ctx.clearRect (0, 0, width, height);
@@ -110,7 +102,7 @@ function render_canvas () {
110102
const noteoffset = 12;
111103
const notescale = height / (123.0 - 2 * noteoffset); // MAX_NOTE
112104
for (const note of pnotes) {
113-
ctx.fillRect (note.tick * tickscale, height - (note.note - noteoffset) * notescale, note.duration * tickscale, 1 * window.devicePixelRatio);
105+
ctx.fillRect (note.tick * tickscale, height - (note.note - noteoffset) * notescale, note.duration * tickscale, 1 * pixelratio);
114106
}
115107
}
116108

0 commit comments

Comments
 (0)