import { runInOp } from "../display/operations.js" import { addToScrollTop } from "../display/scrolling.js" import { regLineChange } from "../display/view_tracking.js" import { heightAtLine, lineIsHidden } from "../line/spans.js" import { lineNo, updateLineHeight } from "../line/utils_line.js" import { widgetHeight } from "../measurement/widgets.js" import { changeLine } from "./changes.js" import { eventMixin } from "../util/event.js" import { signalLater } from "../util/operation_group.js" // Line widgets are block elements displayed above or below a line. export class LineWidget { constructor(doc, node, options) { if (options) for (let opt in options) if (options.hasOwnProperty(opt)) this[opt] = options[opt] this.doc = doc this.node = node } clear() { let cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line) if (no == null || !ws) return for (let i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1) if (!ws.length) line.widgets = null let height = widgetHeight(this) updateLineHeight(line, Math.max(0, line.height - height)) if (cm) { runInOp(cm, () => { adjustScrollWhenAboveVisible(cm, line, -height) regLineChange(cm, no, "widget") }) signalLater(cm, "lineWidgetCleared", cm, this, no) } } changed() { let oldH = this.height, cm = this.doc.cm, line = this.line this.height = null let diff = widgetHeight(this) - oldH if (!diff) return if (!lineIsHidden(this.doc, line)) updateLineHeight(line, line.height + diff) if (cm) { runInOp(cm, () => { cm.curOp.forceUpdate = true adjustScrollWhenAboveVisible(cm, line, diff) signalLater(cm, "lineWidgetChanged", cm, this, lineNo(line)) }) } } } eventMixin(LineWidget) function adjustScrollWhenAboveVisible(cm, line, diff) { if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop)) addToScrollTop(cm, diff) } export function addLineWidget(doc, handle, node, options) { let widget = new LineWidget(doc, node, options) let cm = doc.cm if (cm && widget.noHScroll) cm.display.alignWidgets = true changeLine(doc, handle, "widget", line => { let widgets = line.widgets || (line.widgets = []) if (widget.insertAt == null) widgets.push(widget) else widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget) widget.line = line if (cm && !lineIsHidden(doc, line)) { let aboveVisible = heightAtLine(line) < doc.scrollTop updateLineHeight(line, line.height + widgetHeight(widget)) if (aboveVisible) addToScrollTop(cm, widget.height) cm.curOp.forceUpdate = true } return true }) if (cm) signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle)) return widget }