Skip to content

Commit

Permalink
Merge branch 'observable_from_getters'
Browse files Browse the repository at this point in the history
* 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]>
  • Loading branch information
tim-janik committed Sep 2, 2019
2 parents 57f6a47 + 05ea0d7 commit 9bc8d41
Show file tree
Hide file tree
Showing 9 changed files with 635 additions and 349 deletions.
7 changes: 6 additions & 1 deletion bse/bsepart.cc
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@ part_update_last_tick (BsePart *self)
self->last_tick_SL = last_tick;
BSE_SEQUENCER_UNLOCK ();
g_object_notify ((GObject*) self, "last-tick");
self->as<Bse::PartImpl*>()->emit_event ("notify:last_tick");
bse_part_links_changed (self);
}

Expand All @@ -357,7 +358,10 @@ range_changed_notify_handler (gpointer data)
self->range_min_note = BSE_MAX_NOTE;
self->range_max_note = 0;
if (min_note <= max_note)
g_signal_emit (self, signal_range_changed, 0, tick, duration, min_note, max_note);
{
g_signal_emit (self, signal_range_changed, 0, tick, duration, min_note, max_note);
self->as<Bse::PartImpl*>()->emit_event ("noteschanged");
}
}
handler_id_range_changed = 0;

Expand Down Expand Up @@ -427,6 +431,7 @@ links_changed_notify_handler (gpointer data)
BsePart *self = (BsePart*) sfi_ring_pop_head (&plist_links_changed);
self->links_queued = FALSE;
g_signal_emit (self, signal_links_changed, 0);
self->as<Bse::PartImpl*>()->emit_event ("linkschanged");
}
handler_id_links_changed = 0;

Expand Down
1 change: 1 addition & 0 deletions ebeast/app.html
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@
// utilities.js
import * as Util from './utilities.js'; // depends on Vue
window.Util = Util;
Vue.prototype.observable_from_getters = Util.vue_observable_from_getters;
for (let directivename in Util.vue_directives) // register all utility directives
Vue.directive (directivename, Util.vue_directives[directivename]);
loadstatus.innerText += "Loaded Util...\n";
Expand Down
42 changes: 17 additions & 25 deletions ebeast/b/part-thumb.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,17 @@

<script>
const tick_quant = 384; // FIXME

function observable_part_data () {
const data = {
partname: { getter: c => this.part.get_name(), notify: n => this.part.on ("notify:uname", n), },
lasttick: { getter: c => this.part.get_last_tick(), notify: n => this.part.on ("notify:last_tick", n), },
allnotes: { default: [], notify: n => this.part.on ("noteschanged", n),
getter: async c => Object.freeze (await this.part.list_notes_crossing (0, CONFIG.MAXINT)), },
};
return this.observable_from_getters (data, () => this.part);
}

module.exports = {
name: 'b-part-thumb',
mixins: [ Util.vue_mixins.dom_updates, Util.vue_mixins.hyphen_props ],
Expand All @@ -39,29 +50,10 @@ module.exports = {
index: { type: Number, },
trackindex: { type: Number, },
},
data_tmpl: { partname: "",
allnotes: [],
lasttick: 0,
},
watch: {
part: { immediate: true, async handler (n, o) {
// first send out async queries in parallel
let partname = this.part.get_name();
let lasttick = this.part.get_last_tick();
let allnotes = this.part.list_notes_crossing (0, CONFIG.MAXINT);
// then, await results in batch
partname = await partname;
lasttick = await lasttick;
allnotes = await allnotes;
// finally, assign Vue-reactive data without further suspension (await)
this.partname = partname;
this.lasttick = lasttick;
this.allnotes = allnotes;
} },
},
data() { return observable_part_data.call (this); },
computed: {
tickscale: function() { return 10 / 384.0; }, // FIXME
pxoffset: function() { return this.tick * 10 / 384.0; }, // FIXME
tickscale: function() { return 10 / 384.0; }, // FIXME
pxoffset: function() { return this.tick * this.tickscale; }, // FIXME
canvas_width: function() {
return this.tickscale * Math.floor ((this.lasttick + tick_quant - 1) / tick_quant) * tick_quant;
},
Expand All @@ -77,10 +69,10 @@ module.exports = {
function render_canvas () {
// canvas setup
const canvas = this.$refs['canvas'];
Util.resize_canvas (canvas, canvas.clientWidth, canvas.clientHeight, true);
const pixelratio = Util.resize_canvas (canvas, canvas.clientWidth, canvas.clientHeight, true);
const ctx = canvas.getContext ('2d'), cstyle = getComputedStyle (canvas), csp = cstyle.getPropertyValue.bind (cstyle);
const width = canvas.width, height = canvas.height;
const tickscale = this.tickscale * window.devicePixelRatio;
const tickscale = this.tickscale * pixelratio;
//const width = canvas.clientWidth, height = canvas.clientHeight;
//canvas.width = width; canvas.height = height;
ctx.clearRect (0, 0, width, height);
Expand Down Expand Up @@ -110,7 +102,7 @@ function render_canvas () {
const noteoffset = 12;
const notescale = height / (123.0 - 2 * noteoffset); // MAX_NOTE
for (const note of pnotes) {
ctx.fillRect (note.tick * tickscale, height - (note.note - noteoffset) * notescale, note.duration * tickscale, 1 * window.devicePixelRatio);
ctx.fillRect (note.tick * tickscale, height - (note.note - noteoffset) * notescale, note.duration * tickscale, 1 * pixelratio);
}
}

Expand Down
Loading

0 comments on commit 9bc8d41

Please sign in to comment.