35e06462a0d877011fb2b729e02668c0601566d478a5bf9f1518e78f6470ceb9883edb2924d28c3caea7d6b8f8153ba605a7c39c6d76783d338b45e820958f 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. import { runInOp } from "../display/operations.js"
  2. import { addToScrollTop } from "../display/scrolling.js"
  3. import { regLineChange } from "../display/view_tracking.js"
  4. import { heightAtLine, lineIsHidden } from "../line/spans.js"
  5. import { lineNo, updateLineHeight } from "../line/utils_line.js"
  6. import { widgetHeight } from "../measurement/widgets.js"
  7. import { changeLine } from "./changes.js"
  8. import { eventMixin } from "../util/event.js"
  9. import { signalLater } from "../util/operation_group.js"
  10. // Line widgets are block elements displayed above or below a line.
  11. export class LineWidget {
  12. constructor(doc, node, options) {
  13. if (options) for (let opt in options) if (options.hasOwnProperty(opt))
  14. this[opt] = options[opt]
  15. this.doc = doc
  16. this.node = node
  17. }
  18. clear() {
  19. let cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line)
  20. if (no == null || !ws) return
  21. for (let i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1)
  22. if (!ws.length) line.widgets = null
  23. let height = widgetHeight(this)
  24. updateLineHeight(line, Math.max(0, line.height - height))
  25. if (cm) {
  26. runInOp(cm, () => {
  27. adjustScrollWhenAboveVisible(cm, line, -height)
  28. regLineChange(cm, no, "widget")
  29. })
  30. signalLater(cm, "lineWidgetCleared", cm, this, no)
  31. }
  32. }
  33. changed() {
  34. let oldH = this.height, cm = this.doc.cm, line = this.line
  35. this.height = null
  36. let diff = widgetHeight(this) - oldH
  37. if (!diff) return
  38. if (!lineIsHidden(this.doc, line)) updateLineHeight(line, line.height + diff)
  39. if (cm) {
  40. runInOp(cm, () => {
  41. cm.curOp.forceUpdate = true
  42. adjustScrollWhenAboveVisible(cm, line, diff)
  43. signalLater(cm, "lineWidgetChanged", cm, this, lineNo(line))
  44. })
  45. }
  46. }
  47. }
  48. eventMixin(LineWidget)
  49. function adjustScrollWhenAboveVisible(cm, line, diff) {
  50. if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
  51. addToScrollTop(cm, diff)
  52. }
  53. export function addLineWidget(doc, handle, node, options) {
  54. let widget = new LineWidget(doc, node, options)
  55. let cm = doc.cm
  56. if (cm && widget.noHScroll) cm.display.alignWidgets = true
  57. changeLine(doc, handle, "widget", line => {
  58. let widgets = line.widgets || (line.widgets = [])
  59. if (widget.insertAt == null) widgets.push(widget)
  60. else widgets.splice(Math.min(widgets.length, Math.max(0, widget.insertAt)), 0, widget)
  61. widget.line = line
  62. if (cm && !lineIsHidden(doc, line)) {
  63. let aboveVisible = heightAtLine(line) < doc.scrollTop
  64. updateLineHeight(line, line.height + widgetHeight(widget))
  65. if (aboveVisible) addToScrollTop(cm, widget.height)
  66. cm.curOp.forceUpdate = true
  67. }
  68. return true
  69. })
  70. if (cm) signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle))
  71. return widget
  72. }