November 2024
Abstract
API documentation and development internals of the Anklang project.
Technically, Anklang consists of a user interface front-end based on web technologies (HTML, DOM, CSS, JavaScript, Lit) and a synthesis engine backend written in C++.
The ase/
subdirectory contains the C++ implementation of
the AnklangSynthEngine
executable which contains the core
component for audio data processing and audio plugin handling. It
interfaces with the HTML DOM based user interface via an IPC layer with
JSON messages that reflect the C++ API.
The synthesis engine can load various audio rendering plugins which are executed in audio rendering worker threads. The main synthesis engine thread coordinates synchronization and interafces between the engine and the UI via an IPC interface over a web-socket that uses remote method calls and event delivery marshalled as JSON messages.
Building on Jsonipc, a small serialization framework provided by ase/serialize.hh is used to marshal values, structs, enums and classes to/from JSON. This is used to store preferences and project data. The intended usage is as follows:
std::string jsontext = Ase::json_stringify (somevalue);
bool success = Ase::json_parse (jsontext, somevalue);
// The JSON root will be of type 'object' if somevalue is a class instance
std::string s; // s contains:
= json_stringify (true); // true
s = json_stringify (-0.17); // -0.17
s = json_stringify (32768); // 32768
s = json_stringify (Ase::Error::IO); // "Ase.Error.IO"
s = json_stringify (String ("STRing")); // "STRing"
s = json_stringify (ValueS ({ true, 5, "HI" })); // [true,5,"HI"]
s = json_stringify (ValueR ({ {"a", 1}, {"b", "B"} })); // {"a":1,"b":"B"} s
In the above examples, Ase::Error::IO
can be serialized
because it is registered as Jsonipc::Enum<Ase::Error>
with its enum values. The same works for serializable classes registered
through Jsonipc::Serializable<SomeClass>
.
[_] Serialization of class instances will have to depend on the
Scope/InstanceMap, so instance pointers in copyable classes registered
as Jsonipc::Serializable<>
can be marshalled into a
JsonValue (as {$id,$class}
pair), then be resolved into an
InstanceP stored in an Ase::Value and from there be marshalled into a
persistent relative object link for project data storage.
Jsonipc is a header-only IPC layer that marshals C++ calls to JSON messages defined in jsonipc/jsonipc.hh. The needed registration code is very straight forward to write manually, but can also be auto-genrated by using jsonipc/cxxjip.py which parses the exported API using CastXML.
The Anklang API for remote method calls is defined in api.hh. Each class with its methods, struct with its fields and enum with its values is registered as a Jsonipc interface using conscise C++ code that utilizes templates to derive the needed type information.
The corresponding Javascript code to use api.hh
via async
remote method calls is generated via
Jsonipc::ClassPrinter::to_string()
by
AnklangSynthEngine --js-api
.
shared_ptr<Class> from_json()
- lookup by id
in InstanceMap or use Scope::make_shared
for
Serializable.to_json (const shared_ptr<Class> &p)
-
marshal Serializable or {id} from InstanceMap.Class* from_json()
- return
&*shared_ptr<Class>
to_json (Class *r)
- supports Serializable or
Class->shared_from_this()
wrapping.Class& from_json()
- return
*shared_ptr<Class>
, throws on nullptr
.
!!!to_json (const Class &v)
- return
to_json<Class*>()
jsonvalue_as_string()
for debugging
purposes.Javascript can register/unregister remote Callbacks with create and remove. C++ sends events to inform about a remote Callback being called or unregistered killed.
void Jsonapi/Trigger/create (id); // JS->C++
void Jsonapi/Trigger/remove (id); // JS->C++
void Jsonapi/Trigger/_<id> ([...]); // C++->JS
void Jsonapi/Trigger/killed (id); // C++->JS
Ase::SharedBase
|
+Ase::Emittable
|
+Ase::Property
|
+Ase::Object
|
+Ase::Gadget
| |
| +Ase::Device
| | |
| | +Ase::NativeDevice
| | | |
| | | +Ase::NativeDeviceImpl
| | |
| | +Ase::Track
| | | |
| | | +Ase::TrackImpl
| | |
| | +Ase::Project
| | | |
| | | +Ase::ProjectImpl
| | |
| | +Ase::ClapDeviceImpl
| |
| +Ase::Clip
| | |
| | +Ase::ClipImpl
| |
| +Ase::Monitor
| | |
| | +Ase::MonitorImpl
| |
| +Ase::Server
| | |
| | +Ase::ServerImpl
| |
| +Ase::GadgetImpl
|
+Ase::ResourceCrawler
|
+Ase::FileCrawler
The user interface components used in Anklang are implemented as custom HTML elements and are generally composed of a single file that provides:
Simple components that have no or at most one child element and do not require complex HTML layouts with lit-html can be implemented directly via customElements.define().
Components with complex layouts that need lit-html or that act as containers with several HTMLSlotElements (for multiple types of children) which require a ShadowRoot, should be implemented as LitElements by extending LitComponent (our convenience wrapper around LitElement.
Note that a Lit component is an HTML element, it extends ReactiveElement which always extends HTMLElement and none of the other HTML element interfaces.
Guidelines capturing past experiences:
Create a component to encapsulate possible state and specific UI/UX behavior or API functionality.
Avoid adding components purely for layout or style reasons. Horizontal or vertical flex boxes, grid layout, dialog and menu styling are much better addressed with CSS classes and are more often subject to change than encapsulated behavior.
Use CSS utility classes instead of custom or adhoc styles that have to be given a dedicated name. TailwindCSS gives good guidance here.
Use simple custom element components if that suffices, components that do not expect external children can get away without a shadow DOM.
Components with external children need <slot/>
elements, these require shadow DOM handling as provided by LitElement,
use it via our LitComponent
class.
Focussable elements MUST NOT be buried inside
the shadow DOM tree. It is perfectly fine to add focussable elements as
(external) children to a ready-made web component that has a shadow DOM
with <slot/>
elements, but proper focus handling,
focus related styling, keyboard and focus chain handling is not reliably
possible when focussable elements are inside the shadow
DOM, instead of staying part of the light DOM by adding them
into <slot/>
elements.
Some components are still implemented via Vue and are slowly phased
out. We often use <canvas>
elements for Anklang
specific displays, and Vue canvas handling comes with certain
caveats:
Util.vue_mixins.dom_updates
mixin (now
default) allows to trigger the dom_update()
component
method for $forceUpdate()
invocations and related
events.methods: { dom_update() {}, }
component entry should
be provided that triggers the actual canvas rendering logic.document.fonts.ready
promise, Anklang
re-renders all Vue components via $forceUpdate()
once all
webfonts have been loaded, <canvas>
elements
containing text usually need to re-render themselves in this case.Envue components:
Envue components are created to simplify some of the oddities of
dealing with Vue-3 components. The function
Envue.Component.vue_export
creates a Vue component
definition, so that the Vue component instance ($vm
) is
tied to an Envue.Component
instance ($object
).
Notes:
$object.$vm
.$vm.$object
.$vm.*
fields e.g. from within a
<template/>
definition are forwarded to access
$object.*
fields.observable_from_getters()
.Vue uses a template compiler to construct a render()
function from HTML
<template/>
strings. The Javascript
expressions in templates are sandboxed and limited in scope, but may
refer to Vue component properties that are exposed through
hasOwnProperty()
. In order to support Envue instance
methods and fields in template expressions, all members present after
Envue construction are forwarded into the Vue component.
The <b-aboutdialog> element is a modal [b-dialog] that displays version information about Anklang.
…
show_notice
(
text, timeout)
The <h-flex> element is a horizontal flex container element. See also the Flex visual cheatsheet.
The <v-flex> element is a vertical flex container element. See also the Flex visual cheatsheet.
The <c-grid> element is a simple grid container element. See also Grid Container and the Grid visual cheatsheet.
The <b-choiceinput> element provides a choice popup to choose
from a set of options. It supports the Vue v-model
protocol by emitting an input
event on value changes and
accepting inputs via the value
prop.
[ { icon, label, blurb }... ]
label
is unspecified, it can be fetched from
prop->label
instead.
event.target.value
.
The <b-clipview> element displays a small view of a MIDI clip.
Browse Ase::Crawler contents
…
folder
()
update_inflight
()
assign_utf8path
(
filepath, pickfile)
entry_event
(
event, entry)
filtered_entries
()
select_entry
(
entry)
entry
or
pathentry.value
close_click
(
ev)
The DataBubbleImpl and
DataBubbleIface classes implement the logic required to
extract and display data-bubble=""
tooltip popups on mouse
hover.
…
update
(
element, text)
data-bubble
attribute of element
to
text
or force its callback
callback
(
element, callback)
data-bubble
attribute of element
force
(
element)
data-bubble
to be shown for element
unforce
(
element)
data-bubble
for element
clear
(
element)
data-bubble
attribute, its callback and cancel a
forced bubble
Editor for audio signal devices.
property_groups
(
asyncpropertylist)
Panel for editing of devices.
Component base class for wrapping Vue components.Let
this.$vm
point to the Vue component, and
$vm.$object
point to this
.
Component
(
vm)
this.$vm
point to the Vue component, and
$vm.$object
point to this
.
update
()
observable_from_getters
(
tmpl)
$watch
(
args)
vue_export
(
vue_object)
[static]
forward_access
(
vm, classinstance, ignores)
vm
to access fields on
classinstance
.
vue_export_from_class
(
Class, vue_object)
Class
instance.
The <b-icon> element displays icons from various icon fonts. In
order to style the color of icon font symbols, simply apply the
color
CSS property to this element (styling
fill
as for SVG elements is not needed).
fa
, bc
, mi
,
uc
.
The <b-knob> element provides a knob for scalar inputs. It
supports the Vue v-model
protocol by emitting an input
event on value changes and
accepting inputs via the value
prop.
-1…+1
.
0…+1
if bidir is false
.
The knob is rendered based on an SVG drawing, which is arranged in
such a way that adding rotational transforms to the SVG elements is
sufficient to display varying knob levels. Chrome cannot render
individual SVG nodes into seperate layers (GPU textures) so utilizing
GPU acceleration requires splitting the original SVG into several SVG
layers, each of which can be utilized as a seperate GPU texture with the
CSS setting will-change: transform
.
spin_drag_start
(
element, event, value_callback)
spin_drag_stop
(
event_or_element)
spin_drag_pointermove
(
event)
spin_drag_change
()
spin_drag_granularity
(
event)
The <b-more> element is an indicator for adding or dropping new UI elements.
Noticeboard to post notifications for end users.
The <b-numberinput> element is a field-editor for integer or
floating point number ranges. The input value
will be
constrained to take on an amount between min
and
max
inclusively.
value
can take on.
value
can take on.
true
, numbers are constrained to
integer values.
event.target.value
.
The <b-objecteditor> element is a field-editor for object
input. A copy of the input value is edited, update notifications are
provided via an input
event.
The <b-partlist> element allows to arrange Clip objects for playback.
list_actions
ntool
(
toolmode, drag_event, cursor,
predicate)
notes_canvas_drag_select
(
event, MODE)
The Select Tool allows selection of single notes or groups of notes. Modifier keys can be used to modify the selection behavior.
notes_canvas_drag_paint
(
event, MODE)
With the note Paint Tool, notes can be placed everywhere in the grid by clicking mouse button 1 and possibly keeping it held during drags.
notes_canvas_drag_move
(
event, MODE)
With the note Move Tool, selected notes can be moved clicking mouse button 1 and keeping it held during drags. A copy will be made instead of moving the selected notes if the ctrl key is pressed during drag.
notes_canvas_drag_resize
(
event, MODE)
When the Paint Tool is selected, the right edge of a note can be draged to make notes shorter or longer in duration.
notes_canvas_drag_erase
(
event, MODE)
The Erase Tool allows deletion of all notes selected during a mouse button 1 drag. The deletion can be aborted by the Escape key.
note_hover_body
(
coords, tick, key, notes)
note_hover_tail
(
coords, tick, key, notes)
note_hover_head
(
coords, tick, key, notes)
notes_canvas_tool_from_hover
(
piano_roll, pointerevent)
target_coords
(
event, target)
The <b-piano-roll> element allows note editing.
piano_layout
()
set_canvas_font
(
ctx, size)
paint_piano
()
paint_notes
()
paint_timeline
()
paint_timegrid
(
canvas, with_labels)
The <b-playcontrols> element is a container holding the play and seek controls for a Ase.song.
The <b-positionview> element displays the project transport position pointer and related information.
The global Shell
singleton carries all current UI and
Ase.Project context
…
piano_roll
()
show_spinner
()
hide_spinner
()
The <b-statusbar> element is an area for the display of status messages and UI configuration.
The <b-switchinput> element is a field-editor switch to change between on and off.
event.target.value
.
The <b-textinput> element is a field-editor for text input.
event.target.value
.
The <b-toggle> element implements a simple toggle button for
boolean audio processor input properties. Its value
can be
accessed as a property and valuechange
is emitted on
changes.
true
or false
.
event.target.value
.
This container can render tree structures with collapsible branches.
srgb_from_hsv
(
hue, saturation, value)
hue, saturation, value
.
hsv_from_srgb
(
srgb)
{ hue, saturation, value }
from
srgb
.
zhsl_from
(
srgb, gamut)
{ hue, saturation, lightness }
from
srgb
.
srgb_from_zhsv
(
hue, saturation, value, gamut)
{ hue, saturation, value }
.
hex_from_zhsv
(
hue, saturation, value, gamut)
{ hue, saturation, value }
.
zhsv_from
(
srgb, gamut)
{ hue, saturation, value }
from
srgb
.
srgb_from_zhsl
(
hue, saturation, lightness,
gamut)
{ hue, saturation, value }
.
hex_from_zhsl
(
hue, saturation, lightness,
gamut)
{ hue, saturation, value }
.
color2rgba
(
color)
color
.
zmod_assignop
(
col, prop, op, num, perc)
zmod
(
colorlike, mods)
zmod4
(
colorlike)
zcam4
(
colorlike)
zlerp
(
c1, c2, t)
lgrey
(
lightness)
markdown_to_html
(
element, markdown_text)
element.innerHTML
from markdown_text
font_family_loaded
(
options)
get_uri
(
element)
valid_uri
(
uri)
has_uri
(
element)
text_content
(
element, with_children)
.textContent
with or without children from a DOM
element.
show_modal
(
dialog)
dialog
via showModal() and close it on backdrop
clicks.
Ase::Clip proxy
Global host
instance for scripts.
…
piano_roll_clip
()
script_name
()
script_uuid
()
api_level
()
use_api
(
api_level, script_uuid)
api_level
and provide UUID for
controllers.
register
(
category, label, fun, blurb,
params)
jsextract.js
- Script to extract snippets marked with
JsExtract
node jsextract.js [OPTIONS] [file.js...]
The jsextract.js processes its input files by
looking for markers such as JsExtract.css`...`
and
extracting the template string contents into a corresponding
*.jscss
file. This can be preprocessed or copied into a
corresponding *.css
file, to be loaded via
JsExtract.fetch_css()
.
Install a FocusGuard to allow only a restricted set of elements to get focus.
kbd_modifiers
KeyCode
display_keyname
(
keyname)
hotkey_name_from_event
(
event)
match_key_event
(
event, keyname)
(
keycode)
activeElement
()
list_focusables
(
element)
element
or the document
push_focus_root
(
element, escapecb)
element
and its descendants
remove_focus_root
(
element)
element
previously installed via
push_focus_root()
element_rect
(
element)
keydown_move_focus
(
event)
keydown
events
move_focus
(
dir, subfocus)
forget_focus
(
element)
element
hotkey_handler
(
event)
add_hotkey
(
hotkey, callback,
subtree_element)
remove_hotkey
(
hotkey, callback)
add_key_filter
(
keycode, callback)
remove_key_filter
(
keycode)
add_keymap
(
keymap)
remove_keymap
(
keymap)
(
element)
element
is button-like input
(
element)
element
has inner input navigation
shortcut_lookup
(
mapname, label, shortcut)
label
in mapname
, default
to shortcut
shortcut_dialog
(
mapname, label, shortcut)
An interface extending LitElement with reactive render() and updated() methods.
…
mkstate
(
str_olist)
JsExtract
API to mark template strings for extraction and fetch extracted assets.
JsExtract.css`body { font-weight:bold; }`
node jsextract.js inputfile.js
JsExtract.css``
strings into
inputfile.jscss
.await JsExtract.fetch_css ('/path/to/inputfile.js');
fetch()
API to retrieve the extracted
string as text/css
from '/path/to/inputfile.css'.JsExtract.fetch_css (import.meta);
import.meta.url
.JsExtract.css_url (import.meta);
lit_update_all
(
root)
LitElement
s
css_url
(
base_url)
.css
extension).
fetch_css
(
base_url)
.css
extension) as "text/css"
adopt_component_styles
(
element)
CHROME_UA
chrome_movement_factor
()
event_movement
(
event, shift_swaps)
movementX,movementY
in CSS pixels, independent of
browser quirks.
wheel_delta
(
event, shift_swaps)
{deltaX,deltaY}
with negative values
pointing LEFT/UP and positive values RIGHT/DOWN respectively. For zoom
step interpretation, the x/y pixel values should be reduced via
Math.sign()
. For scales the pixel values might feel more
natural, because browsers sometimes increase the number of events with
increasing wheel distance, in other cases values are accumulated so
fewer events with larger deltas are sent instead. The members
{x,y}
approximate the wheel delta in pixels.
zmove_last
()
zmove_add
(
hook)
zmove_trigger
(
ev)
A ScriptHost object represents a Worker script in the Main thread.
A ScriptHost object (defined in script.js
) runs in the
Main Javascript thread. It starts a Worker via host.js
which creates a WorkerHost object in the Worker thread. The
ScriptHost<->WorkerHost objects bridge needed API from the Main
thread to the Worker thread. The WorkerHost then loads and calls into a
user provided ES Module, the actual user script which communicates via
the WorkerHost global variable host
. See also
#WorkerHost.
count_newlines
(
str)
(
string)
^</tag>
in
string
.
process_file
(
filename, config)
write_style
(
filename, ofile, config,
stylestring)
jsonapi_finalization_registration
(
object)
jsonapi_finalization_gc
(
gcinfo)
hex
(
s, prefix)
s
into 2-digit hex representation.
displayfs
(
utf16pua)
basename
(
path)
path
.
displaybasename
(
path)
path
).
dirname
(
path)
path
.
displaydirname
(
path)
path
).
drag_event(event,MODE)
method
…
vuechildren
()
[static]
$children
(and $vue_parent
) on every
component
autodataattrs
()
[static]
$attrs['data-*']
to $el
dom_updates
()
[static]
Vue mixin to provide DOM handling hooks. This mixin adds instance
method callbacks to handle dynamic DOM changes such as drawing into a
<canvas/>
. Reactive callback methods have their data
dependencies tracked, so future changes to data dependencies of reactive
methods will queue future updates. However reactive dependency tracking
only works for non-async methods.
this.$el
has been
createdthis.$el
has been
reassigned or changed. Note, may also be called for
v-if="false"
cases.this.$el
and after Vue component updates. Dependency
changes result in this.$forceUpdate()
.dom_queue_draw()
. Dependency changes
result in this.dom_queue_draw()
.this.dom_draw()
to be called
during the next animation frame.this.$el
is removed.clone_descriptors
now
()
frame_stamp
()
debounce
(
callback, options)
Yield a wrapper function for callback
that throttles
invocations. Regardless of the frequency of calls to the returned
wrapper, callback
will only be called once per
requestAnimationFrame()
cycle, or after
milliseconds
. The return value of the wrapper functions is
the value of the last callback invocation. A cancel()
method can be called on the returned wrapper to cancel the next pending
callback
invocation. Options:
wait
- number of milliseconds to pass until
callback
may be called.restart
- always restart the timer once the wrapper is
called.immediate
- immediately invoke callback
and then start the timeout period.capture_event
(
eventname, callback)
eventname
with a single callback
exclusively
coalesced_events
(
event)
event
into a list of possibly coalesced
events
vue_component
(
element)
element
or its ancestors
envue_object
(
element)
$object
from element or its ancestors
drag_event
(
event)
drag_event (event)
handling on a Vue component's
element, use @pointerdown="drag_event"
unrequest_pointer_lock
(
element)
element
if any and ensure it does not get a pointer lock
granted unless request_pointer_lock() is called on it again.
has_pointer_lock
(
element)
Check if element
has a (pending) pointer lock
Return:
element
has the pointer lock;request_pointer_lock
(
element)
element
and track its state Use
this function to maintain pointer locks to avoid stuck locks that can
get granted after exitPointerLock() has been called.
adopt_style
(
element, csstext,
stylesheet_name)
element
contains a
csstext
(replaceable via stylesheet_name
)
add_style_sheet
(
element, url)
element
has a url
stylesheet link
vm_scope_selector
(
vm)
vm_scope_style
(
vm, css)
css
to Vue instance vm
, use
vm_scope_selector() for the vm
CSS scope
assign_forin
(
target, source)
source
and assign to
target
assign_forof
(
target, source)
source
and assign to
target
array_remove
(
array, item)
item
from array
if present via
indexOf
array_index_equals
(
array, item)
array
index of element that equals item
map_from_kvpairs
(
kvarray)
kvarray
range
(
bound, end, step)
bound
[ if one arg is given or
[bound
..end
[ by incrementing step
freeze_deep
(
object)
copy_deep
(
src)
src
equals_recursively
(
a, b)
a == b
, recursively if the arguments are of type
Array or Object
clamp
(
x, min, max)
fwdprovide
(
injectname, keys)
hyphenate
(
string)
weakid
(
object)
weakid_lookup
(
id)
uuname
(
object)
join_classes
(
args)
args
object_zip
(
obj, keys, values)
obj[k] = v
for all
k of keys, v of values
object_await_values
(
obj)
extend_property
(
prop, disconnector, augment)
.value_
updates, a disconnector
function must
be provided as second argument, to handle disconnection of property
change notifications once the property is not needed anymore.
promise_state
(
p)
p
state as one of: 'pending',
'fulfilled', 'rejected'
compile_expression
(
expression, context)
$event
handler expression into a function. This
yields a factory function that binds the scope to create an expression
handler.
VueifyObject
(
object, vue_options)
data
object.
Properties with a getter (and possibly setter) are turned into Vue
computed
properties, methods are carried over as
methods
on the Vue() instance.
fnv1a_hash
(
str)
split_comma
(
str)
escape_html
(
unsafe)
&
and related sequences
parse_hex_color
(
colorstr)
parse_hex_luminosity
(
colorstr)
parse_hex_brightness
(
colorstr)
parse_hex_pgrey
(
colorstr)
parse_hex_average
(
colorstr)
parse_colors
(
colorstr)
compute_style_properties
(
el, obj)
obj
resolved
against the style of el
check_visibility
(
element)
element
is_displayed
(
element)
element
is displayed (has width/height assigned)
setup_shield_element
(
shield, containee, closer,
capture_escape)
containee
, call closer(event)
for
pointer clicks on shield
or when ESCAPE
is
pressed.
swallow_event
(
type, timeout)
type
events until
timeout
has passed
prevent_event
(
event_or_null)
dialog_backdrop_mousedown
(
ev)
dialog_backdrop_mouseup
(
ev)
dialog_backdrop_autoclose
(
dialog, install_or_remove)
popup_position
(
element, opts)
resize_canvas
(
canvas, csswidth, cssheight,
fill_style)
dash_xto
(
ctx, x, y, w, d)
w
with dashes
d
hstippleRect
(
ctx, x, y, width, height,
stipple)
(x,y,width,height)
with pixel gaps
of width stipple
roundRect
(
ctx, x, y, width, height, radius, fill,
stroke)
gradient_apply_stops
(
grad, stoparray)
stoparray
to grad
,
stoparray
is an array: [(offset,color)...]
linear_gradient_from
(
ctx, stoparray, x1, y1, x2, y2)
stoparray
canvas_ink_vspan
(
font_style, textish)
midi_label
(
numish)
align8
(
int)
telemetry_subscribe
(
fun, telemetryfields)
fun
for telemtry updates, returns unsubscribe handler
telemetry_unsubscribe
(
telemetryobject)
fun
for telemtry updates, returns unsubscribe handler
in_keyboard_click
()
keyboard_click_event
(
fallback)
keyboard_click
(
element, event, callclick)
in_array
(
element, array)
element
is contained in array
matches_forof
(
element, iteratable)
element
is found during
for (... of iteratable)
element_text
(
element, filter)
(
menu, uri, title)
uri
keyboard_map_name
(
keyname)
has_ancestor
(
node, ancestor,
escape_shadowdom)
ancestor
is an ancestor of node
,
maybe including shadowRoot elements
closest
(
element, selector)
selector
,
traversing shadow DOMs
root_ancestor
(
element)
element
find_element_from_point
(
root, x, y, predicate, visited)
(x,y)
for which
predicate (element)
is true
create_note
(
text, timeout)
assign_async_cleanup
(
map, key, cleaner)
map[key] = cleaner
, while awaiting and calling any
previously existing cleanup function
observable_force_update
()
observable_from_getters()
result to
force updates
observable_from_getters
(
tmpl, predicate)
Create a reactive dict from the fields in tmpl
with
async callbacks.
Once the resolved result from predicate()
changes and
becomes true-ish, the getter()
of each field in
tmpl
is called, resolved and assigned to the corresponding
field in the observable binding returned from this function. Optionally,
fields may provide a notify
setup handler to install a
notification callback that re-invokes the getter
. A
destructor can be returned from notify()
once resolved,
that is executed during cleanup phases. The default
of each
field in tmpl
may provide an initial value before
getter
is called the first time and in case
predicate()
becomes false-ish. The first argument to
getter()
is a function that can be used to register cleanup
code for the getter result.
const data = {
val: { getter: c => async_fetch(), notify: n => add_listener (n), },
;
}= this.observable_from_getters (data, () => this.predicate());
dict // use dict.val
When the n()
callback is called, a new getter
call is scheduled. A handler can be registered with
c (cleanup);
to cleanup resources left over from an
async_fetch()
call.
tmplstr
(
a, e)
strpad
(
string, len, fill)
string
with fill
until its length is
len
lrstrip
(
str)
collect_text_content
(
node_or_array)
node_or_array
hash53
(
key, seed)
key
add_destroy_callback
(
callback)
callback
to this
to be called from
call_destroy_callbacks()
del_destroy_callback
(
callback)
callback
from this
, previously added
via add_destroy_callback()
call_destroy_callbacks
()
this
, clears the callback list
Caching wrapper for ASE classes
…
__add__
(
prop, defaultvalue, callback)
__del__
(
prop, callback)
__cleanup__
()
finalization_cleanup_registry
wrap_ase_object
(
aseobj, fields, callback)
define_reactive
(
object, properties_object)
object
, to be used with
reactive_wrapper(). See also Object.defineProperties.
reactive_wrapper
(
effect, notifier, keepwatching)
effect()
wrapper to watch reactive properties, on
changes run notifier()
.
lrstrip
(
str)
fix_indent
(
txt)
\n\s\*\s
extract_md
(
txt)
process_file
(
filename, config)
Releases of the Anklang project are hosted on GitHub under Anklang Releases. A release encompasses a distribution tarball that has the release version number baked into the misc/version.sh script.
The Anklang project uses
MAJOR.MINOR.MICRO[.DEVEL][-SUFFIX]
version
numbers with the folloing uses:
MAJOR
- The major number is currently
0, so all bets are off. It is planned to signify major changes to
users.MINOR
- The minor number indicates
significant changes, often these are user visible improvements.MICRO
- The micro number increases
with every release.DEVEL
- The devel part is optional and
increases with every new commit, it numbers builds between official
releases. The presence of the [.DEVEL]
part indicates a
version ordered after its corresponding
MAJOR.MINOR.MICRO
release.SUFFIX
- An optional suffix is
sometimes used for e.g. release candidates. The presence of the
[-SUFFIX]
part indicates a version ordered before
its corresponding MAJOR.MINOR.MICRO
release.Git tags are used to store release versions, development versions are
derived from those tags similar to how git describe
works.
The current version can always be obtained by invoking
misc/version.sh
.
The script misc/mkassets.sh
can be used to create and
clean up a release build directory and it triggers the necessary rules
to create a distribution tarball and to build the release assets. All
assets are built from the distribution tarball without any Git
dependency. Producing a distribution tarball depends on Git however.
With four sample values \(V_0\), \(V_1\), \(V_2\) and \(V_3\), cubic interpolation approximates the curve segment connecting \(V_1\) and \(V_2\), by using the beginning and ending slope, the curvature and the rate of curvature change to construct a cubic polynomial.
The cubic polynomial starts out as:
Where \(0 <= x <= 1\), specifying the sample value of the curve segment between \(V_1\) and \(V_2\) to obtain.
To calculate the coefficients \(w_0…w_3\), we set out the following conditions:
We obtain \(V_1'\) and \(V_2'\) from the respecting slope triangles:
With (f)→(d) and (g)→(e) we get:
The derivation of \(f(x)\) is:
From \(x=0\) →(a), i.e. (b), we obtain \(w_0\) and from \(x=0\) →(j), i.e. (h), we obtain \(w_1\). With \(w_0\) and \(w_1\) we can solve the linear equation system formed by (c)→(a) and (e)→(j) to obtain \(w_2\) and \(w_3\).
\((c)→(a): w_3 + w_2 + \frac {V_2 - V_0} {2} + V_1 = V_2\)
\((e)→(j): 3 w_3 + 2 w_2 + \frac {V_2 - V_0} {2} = \frac {V_3 - V_1} {2}\)
With the resulting coefficients:
\[ \begin{aligned} w_0 &= V_1 & &(initial\:value) \\ w_1 &= \frac{V_2 - V_0} {2} & &(initial\:slope) \\ w_2 &= \frac{-V_3 + 4 V_2 - 5 V_1 + 2 V_0} {2} & &(initial\:curvature) \\ w_3 &= \frac{V_3 - 3 V_2 + 3 V_1 - V_0} {2} & &(rate\:change\:of\:curvature) \end{aligned} \]
Reformulating (a) to involve just multiplications and additions (eliminating power), we get:
Based on \(V_0…V_3\), \(w_0…w_3\) and (k), we can now approximate all values of the curve segment between \(V_1\) and \(V_2\).
However, for practical resampling applications where only a specific precision is required, the number of points we need out of the curve segment can be reduced to a finite amount. Lets assume we require \(n\) equally spread values of the curve segment, then we can precalculate \(n\) sets of \(W_{0…3}[i]\), \(i=[0…n]\), coefficients to speed up the resampling calculation, trading memory for computational performance. With \(w_{0…3}\) in (a):
\[ \begin{alignedat} {2} f(x) \ &= & \frac{V_3 - 3 V_2 + 3 V_1 - V_0} 2 x^3 \ + & \\ & & \frac{-V_3 + 4 V_2 - 5 V_1 + 2 V_0} 2 x^2 \ + & \\ & & \frac{V_2 - V_0} 2 x \ + & \\ & & V1 \ \ \ \ & \end{alignedat} \]
sorted for \(V_0…V_4\), we have:
With (l) we can solve \(f(x)\) for all \(x = \frac i n\), where \(i = [0, 1, 2, …, n]\) by substituting \(g(i) = f(\frac i n)\) with
and using \(n\) precalculated coefficients \(W_{0…3}\) according to:
\[ \begin{alignedat}{4} m &= \frac i n \\ W_3[i] &=& 0.5 m^3 & - & 0.5 m^2 & & \\ W_2[i] &=& -1.5 m^3 & + & 2 m^2 & + 0.5 m & \\ W_1[i] &=& 1.5 m^3 & - & 2.5 m^2 & & + 1 \\ W_0[i] &=& -0.5 m^3 & + & m^2 & - 0.5 m & \end{alignedat} \]
We now need to setup \(W_{0…3}[0…n]\) only once, and are then able to obtain up to \(n\) approximation values of the curve segment between \(V_1\) and \(V_2\) with four multiplications and three additions using (m), given \(V_0…V_3\).
There seems to be a lot of inconsistency in the behaviour of modifiers (shift and/or control) with regards to GUI operations like selections and drag and drop behaviour.
According to the Gtk+ implementation, modifiers relate to DND operations according to the following list:
Modifier | Operation | Note / X-Cursor | |
---|---|---|---|
none | → | copy | (else move (else link)) |
SHIFT |
→ | move | GDK_FLEUR |
CTRL |
→ | copy | GDK_PLUS ,
GDK_CROSS |
SHIFT+CTRL |
→ | link | GDK_UL_ANGLE |
Regarding selections, the following email provides a short summary:
From: Tim Janik timj@gtk.org To: Hacking Gnomes Gnome-Hackers@gnome.org Subject: modifiers for the second selection Message-ID: Pine.LNX.4.21.0207111747190.12292-100000@rabbit.birnet.private Date: Thu, 11 Jul 2002 18:10:52 +0200 (CEST)
hi all,
in the course of reimplementing drag-selection for a widget, i did a small survey of modifier behaviour in other (gnome/ gtk) programs and had to figure that there's no current standard behaviour to adhere to:
for all applications, the first selection works as expected, i.e. press-drag-release selects the region (box) the mouse was draged over. also, starting a new selection without pressing any modifiers simply replaces the first one. differences occour when holding a modifier (shift or ctrl) when starting the second selection.
Gimp: Shift upon button press: the new seleciton is added to the existing one Ctrl upon button press: the new selection is subtracted from the existing one Shift during drag: the selection area (box or circle) has fixed aspect ratio Ctrl during drag: the position of the initial button press serves as center of the selected box/circle, rather than the upper left corner
Gnumeric: Shift upon button press: the first selection is resized Ctrl upon button press: the new seleciton is added to the existing one
Abiword (selecting text regions): Shift upon button press: the first selection is resized Ctrl upon button press: triggers a compound (word) selection that replaces the first selection
Mozilla (selecting text regions): Shift upon button press: the first selection is resized
Nautilus: Shift or Ctrl upon buttn press: the new selection is added to or subtracted from the first selection, depending on whether the newly selected region was selected before. i.e. implementing XOR integration of the newly selected area into the first.
i'm not pointing this out to start a flame war over what selection style is good or bad and i do realize that different applications have different needs (i.e. abiword does need compound selection, and the aspect-ratio/centering style for gimp wouldn't make too much sense for text), but i think for the benfit of the (new) users, there should me more consistency regarding modifier association with adding/subtracting/resizing/xoring to/from existing selections.
ciaoTJ