Decoupled History from QML
This commit is contained in:
parent
54363b25bc
commit
2dc9234b22
10 changed files with 239 additions and 191 deletions
|
@ -54,29 +54,41 @@ export class BaseEventEmitter {
|
|||
* @param {function(BaseEvent)} eventListener - The function to be called back when the event is emitted.
|
||||
*/
|
||||
on(eventType, eventListener) {
|
||||
if(!this.constructor.emits.includes(eventType)) {
|
||||
const className = this.constructor.name
|
||||
const eventTypes = this.constructor.emits.join(", ")
|
||||
throw new Error(`Cannot listen to unknown event ${eventType} in class ${className}. ${className} only emits: ${eventTypes}`)
|
||||
if(eventType.includes(" ")) // Listen to several different events with the same listener.
|
||||
for(const type of eventType.split(" "))
|
||||
this.on(type, eventListener)
|
||||
else {
|
||||
console.log("Listening to", eventType)
|
||||
if(!this.constructor.emits.includes(eventType)) {
|
||||
const className = this.constructor.name
|
||||
const eventTypes = this.constructor.emits.join(", ")
|
||||
throw new Error(`Cannot listen to unknown event ${eventType} in class ${className}. ${className} only emits: ${eventTypes}`)
|
||||
}
|
||||
if(!this.#listeners[eventType].has(eventListener))
|
||||
this.#listeners[eventType].add(eventListener)
|
||||
}
|
||||
if(!this.#listeners[eventType].has(eventListener))
|
||||
this.#listeners[eventType].add(eventListener)
|
||||
}
|
||||
|
||||
/**
|
||||
* Remvoes a listener from an event that can be emitted by this object.
|
||||
* Removes a listener from an event that can be emitted by this object.
|
||||
*
|
||||
* @param {string} eventType - Name of the event that was listened to. Throws an error if this object does not emit this kind of event.
|
||||
* @param {function(BaseEvent)} eventListener - The function previously registered as a listener.
|
||||
* @returns {boolean} True if the listener was removed, false if it was not found.
|
||||
*/
|
||||
off(eventType, eventListener) {
|
||||
if(!this.constructor.emits.includes(eventType)) {
|
||||
const className = this.constructor.name
|
||||
const eventTypes = this.constructor.emits.join(", ")
|
||||
throw new Error(`Cannot listen to unknown event ${eventType} in class ${className}. ${className} only emits: ${eventTypes}`)
|
||||
if(eventType.includes(" ")) { // Unlisten to several different events with the same listener.
|
||||
let found = false
|
||||
for(const type of eventType.split(" "))
|
||||
found ||= this.off(eventType, eventListener)
|
||||
} else {
|
||||
if(!this.constructor.emits.includes(eventType)) {
|
||||
const className = this.constructor.name
|
||||
const eventTypes = this.constructor.emits.join(", ")
|
||||
throw new Error(`Cannot listen to unknown event ${eventType} in class ${className}. ${className} only emits: ${eventTypes}`)
|
||||
}
|
||||
return this.#listeners[eventType].delete(eventListener)
|
||||
}
|
||||
return this.#listeners[eventType].delete(eventListener)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -67,6 +67,19 @@ function stringReplaceAll(from, to) {
|
|||
return this.split(from).join(to)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of an element of the array at a given index.
|
||||
* Accepts negative indexes.
|
||||
* @this {Array|string}
|
||||
* @param {number} index
|
||||
* @return {*}
|
||||
*/
|
||||
function arrayAt(index) {
|
||||
if(typeof index !== "number")
|
||||
throw new Error(`${index} is not a number`)
|
||||
return index >= 0 ? this[index] : this[this.length + index]
|
||||
}
|
||||
|
||||
|
||||
const polyfills = {
|
||||
2017: [
|
||||
|
@ -95,8 +108,8 @@ const polyfills = {
|
|||
[String.prototype, "replaceAll", stringReplaceAll]
|
||||
],
|
||||
2022: [
|
||||
[Array.prototype, "at", notPolyfilled("Array.prototype.at")],
|
||||
[String.prototype, "at", notPolyfilled("String.prototype.at")],
|
||||
[Array.prototype, "at", arrayAt],
|
||||
[String.prototype, "at", arrayAt],
|
||||
[Object, "hasOwn", notPolyfilled("Object.hasOwn")]
|
||||
],
|
||||
2023: [
|
||||
|
|
|
@ -17,60 +17,151 @@
|
|||
*/
|
||||
|
||||
import { Module } from "./common.mjs"
|
||||
import { HistoryInterface, NUMBER, STRING } from "./interface.mjs"
|
||||
import { HelperInterface, HistoryInterface, NUMBER, STRING } from "./interface.mjs"
|
||||
import { BaseEvent } from "../events.mjs"
|
||||
import { Action, Actions } from "../history/index.mjs"
|
||||
|
||||
|
||||
|
||||
class UpdatedEvent extends BaseEvent {
|
||||
constructor() {
|
||||
super("updated")
|
||||
}
|
||||
}
|
||||
|
||||
class UndoneEvent extends BaseEvent {
|
||||
constructor(action) {
|
||||
super("undone")
|
||||
this.undid = action
|
||||
}
|
||||
}
|
||||
|
||||
class RedoneEvent extends BaseEvent {
|
||||
constructor(action) {
|
||||
super("redone")
|
||||
this.redid = action
|
||||
}
|
||||
}
|
||||
|
||||
class HistoryAPI extends Module {
|
||||
static emits = ["updated", "undone", "redone"]
|
||||
|
||||
#helper
|
||||
|
||||
constructor() {
|
||||
super("History", {
|
||||
historyObj: HistoryInterface,
|
||||
helper: HelperInterface,
|
||||
themeTextColor: STRING,
|
||||
imageDepth: NUMBER,
|
||||
fontSize: NUMBER
|
||||
})
|
||||
// History QML object
|
||||
this.history = null
|
||||
/** @type {Action[]} */
|
||||
this.undoStack = []
|
||||
/** @type {Action[]} */
|
||||
this.redoStack = []
|
||||
|
||||
this.themeTextColor = "#FF0000"
|
||||
this.imageDepth = 2
|
||||
this.fontSize = 28
|
||||
}
|
||||
|
||||
initialize({ historyObj, themeTextColor, imageDepth, fontSize }) {
|
||||
super.initialize({ historyObj, themeTextColor, imageDepth, fontSize })
|
||||
this.history = historyObj
|
||||
/**
|
||||
* @param {HelperInterface} historyObj
|
||||
* @param {string} themeTextColor
|
||||
* @param {number} imageDepth
|
||||
* @param {number} fontSize
|
||||
*/
|
||||
initialize({ helper, themeTextColor, imageDepth, fontSize }) {
|
||||
super.initialize({ helper, themeTextColor, imageDepth, fontSize })
|
||||
this.#helper = helper
|
||||
this.themeTextColor = themeTextColor
|
||||
this.imageDepth = imageDepth
|
||||
this.fontSize = fontSize
|
||||
}
|
||||
|
||||
/**
|
||||
* Undoes the Action at the top of the undo stack and pushes it to the top of the redo stack.
|
||||
*/
|
||||
undo() {
|
||||
if(!this.initialized) throw new Error("Attempting undo before initialize!")
|
||||
this.history.undo()
|
||||
if(this.undoStack.length > 0) {
|
||||
const action = this.undoStack.pop()
|
||||
action.undo()
|
||||
this.redoStack.push(action)
|
||||
this.emit(new UndoneEvent(action))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Redoes the Action at the top of the redo stack and pushes it to the top of the undo stack.
|
||||
*/
|
||||
redo() {
|
||||
if(!this.initialized) throw new Error("Attempting redo before initialize!")
|
||||
this.history.redo()
|
||||
if(this.redoStack.length > 0) {
|
||||
const action = this.redoStack.pop()
|
||||
action.redo()
|
||||
this.undoStack.push(action)
|
||||
this.emit(new RedoneEvent(action))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears both undo and redo stacks completely.
|
||||
*/
|
||||
clear() {
|
||||
if(!this.initialized) throw new Error("Attempting clear before initialize!")
|
||||
this.history.clear()
|
||||
this.undoStack = []
|
||||
this.redoStack = []
|
||||
this.emit(new UpdatedEvent())
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an instance of HistoryLib.Action to history.
|
||||
* @param action
|
||||
*/
|
||||
addToHistory(action) {
|
||||
if(!this.initialized) throw new Error("Attempting addToHistory before initialize!")
|
||||
this.history.addToHistory(action)
|
||||
if(action instanceof Action) {
|
||||
console.log("Added new entry to history: " + action.getReadableString())
|
||||
this.undoStack.push(action)
|
||||
if(this.#helper.getSettingBool("reset_redo_stack"))
|
||||
this.redoStack = []
|
||||
this.emit(new UpdatedEvent())
|
||||
}
|
||||
}
|
||||
|
||||
unserialize(...data) {
|
||||
/**
|
||||
* Unserializes both the undo stack and redo stack from serialized content.
|
||||
* @param {[string, any[]][]} undoSt
|
||||
* @param {[string, any[]][]} redoSt
|
||||
*/
|
||||
unserialize(undoSt, redoSt) {
|
||||
if(!this.initialized) throw new Error("Attempting unserialize before initialize!")
|
||||
this.history.unserialize(...data)
|
||||
this.clear()
|
||||
for(const [name, args] of undoSt)
|
||||
this.undoStack.push(
|
||||
new Actions[name](...args)
|
||||
)
|
||||
for(const [name, args] of redoSt)
|
||||
this.redoStack.push(
|
||||
new Actions[name](...args)
|
||||
)
|
||||
this.emit(new UpdatedEvent())
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes history into JSON-able content.
|
||||
* @return {[[string, any[]], [string, any[]]]}
|
||||
*/
|
||||
serialize() {
|
||||
if(!this.initialized) throw new Error("Attempting serialize before initialize!")
|
||||
return this.history.serialize()
|
||||
let undoSt = [], redoSt = [];
|
||||
for(const action of this.undoStack)
|
||||
undoSt.push([ action.type(), action.export() ])
|
||||
for(const action of this.redoStack)
|
||||
redoSt.push([ action.type(), action.export() ])
|
||||
return [undoSt, redoSt]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -60,24 +60,6 @@ export class Interface {
|
|||
}
|
||||
|
||||
|
||||
export class SettingsInterface extends Interface {
|
||||
width = NUMBER
|
||||
height = NUMBER
|
||||
xmin = NUMBER
|
||||
ymax = NUMBER
|
||||
xzoom = NUMBER
|
||||
yzoom = NUMBER
|
||||
xaxisstep = STRING
|
||||
yaxisstep = STRING
|
||||
xlabel = STRING
|
||||
ylabel = STRING
|
||||
linewidth = NUMBER
|
||||
textsize = NUMBER
|
||||
logscalex = BOOLEAN
|
||||
showxgrad = BOOLEAN
|
||||
showygrad = BOOLEAN
|
||||
}
|
||||
|
||||
export class CanvasInterface extends Interface {
|
||||
imageLoaders = OBJECT
|
||||
/** @type {function(string): CanvasRenderingContext2D} */
|
||||
|
|
|
@ -21,7 +21,7 @@ import Objects from "./objects.mjs"
|
|||
import History from "./history.mjs"
|
||||
import Canvas from "./canvas.mjs"
|
||||
import Settings from "./settings.mjs"
|
||||
import { DialogInterface, RootInterface, SettingsInterface } from "./interface.mjs"
|
||||
import { DialogInterface, RootInterface } from "./interface.mjs"
|
||||
|
||||
|
||||
class IOAPI extends Module {
|
||||
|
|
|
@ -72,7 +72,11 @@ class SettingsAPI extends Module {
|
|||
helper: HelperInterface
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {HelperInterface} helper
|
||||
*/
|
||||
initialize({ helper }) {
|
||||
super.initialize({ helper })
|
||||
// Initialize default values.
|
||||
|
|
|
@ -76,18 +76,16 @@ MenuBar {
|
|||
Action {
|
||||
text: qsTr("&Undo")
|
||||
shortcut: StandardKey.Undo
|
||||
onTriggered: history.undo()
|
||||
onTriggered: Modules.History.undo()
|
||||
icon.name: 'edit-undo'
|
||||
icon.color: enabled ? sysPalette.windowText : sysPaletteIn.windowText
|
||||
enabled: history.undoCount > 0
|
||||
}
|
||||
Action {
|
||||
text: qsTr("&Redo")
|
||||
shortcut: StandardKey.Redo
|
||||
onTriggered: history.redo()
|
||||
onTriggered: Modules.History.redo()
|
||||
icon.name: 'edit-redo'
|
||||
icon.color: enabled ? sysPalette.windowText : sysPaletteIn.windowText
|
||||
enabled: history.redoCount > 0
|
||||
}
|
||||
Action {
|
||||
text: qsTr("&Copy plot")
|
||||
|
|
|
@ -64,10 +64,7 @@ Item {
|
|||
Clears both undo and redo stacks completly.
|
||||
*/
|
||||
function clear() {
|
||||
undoCount = 0
|
||||
redoCount = 0
|
||||
undoStack = []
|
||||
redoStack = []
|
||||
Modules.History.clear()
|
||||
}
|
||||
|
||||
|
||||
|
@ -76,18 +73,7 @@ Item {
|
|||
Serializes history into JSON-able content.
|
||||
*/
|
||||
function serialize() {
|
||||
let undoSt = [], redoSt = [];
|
||||
for(let i = 0; i < undoCount; i++)
|
||||
undoSt.push([
|
||||
undoStack[i].type(),
|
||||
undoStack[i].export()
|
||||
]);
|
||||
for(let i = 0; i < redoCount; i++)
|
||||
redoSt.push([
|
||||
redoStack[i].type(),
|
||||
redoStack[i].export()
|
||||
]);
|
||||
return [undoSt, redoSt]
|
||||
return Modules.History.serialize()
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -95,14 +81,7 @@ Item {
|
|||
Unserializes both \c undoSt stack and \c redoSt stack from serialized content.
|
||||
*/
|
||||
function unserialize(undoSt, redoSt) {
|
||||
clear();
|
||||
for(let i = 0; i < undoSt.length; i++)
|
||||
undoStack.push(new JS.HistoryLib.Actions[undoSt[i][0]](...undoSt[i][1]))
|
||||
for(let i = 0; i < redoSt.length; i++)
|
||||
redoStack.push(new JS.HistoryLib.Actions[redoSt[i][0]](...redoSt[i][1]))
|
||||
undoCount = undoSt.length;
|
||||
redoCount = redoSt.length;
|
||||
objectLists.update()
|
||||
Modules.History.unserialize(undoSt, redoSt)
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -110,16 +89,7 @@ Item {
|
|||
Adds an instance of HistoryLib.Action to history.
|
||||
*/
|
||||
function addToHistory(action) {
|
||||
if(action instanceof JS.HistoryLib.Action) {
|
||||
console.log("Added new entry to history: " + action.getReadableString())
|
||||
undoStack.push(action)
|
||||
undoCount++;
|
||||
if(Helper.getSettingBool("reset_redo_stack")) {
|
||||
redoStack = []
|
||||
redoCount = 0
|
||||
}
|
||||
saved = false
|
||||
}
|
||||
Modules.History.addToHistory(action)
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -128,16 +98,7 @@ Item {
|
|||
By default, will update the graph and the object list. This behavior can be disabled by setting the \c updateObjectList to false.
|
||||
*/
|
||||
function undo(updateObjectList = true) {
|
||||
if(undoStack.length > 0) {
|
||||
var action = undoStack.pop()
|
||||
action.undo()
|
||||
if(updateObjectList)
|
||||
objectLists.update()
|
||||
redoStack.push(action)
|
||||
undoCount--;
|
||||
redoCount++;
|
||||
saved = false
|
||||
}
|
||||
Modules.History.undo()
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -146,77 +107,6 @@ Item {
|
|||
By default, will update the graph and the object list. This behavior can be disabled by setting the \c updateObjectList to false.
|
||||
*/
|
||||
function redo(updateObjectList = true) {
|
||||
if(redoStack.length > 0) {
|
||||
var action = redoStack.pop()
|
||||
action.redo()
|
||||
if(updateObjectList)
|
||||
objectLists.update()
|
||||
undoStack.push(action)
|
||||
undoCount++;
|
||||
redoCount--;
|
||||
saved = false
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\qmlmethod void History::undoMultipleDefered(int toUndoCount)
|
||||
Undoes several HistoryLib.Action at the top of the undo stack and pushes them to the top of the redo stack.
|
||||
It undoes them deferedly to avoid overwhelming the computer while creating a cool short accelerated summary of all changes.
|
||||
*/
|
||||
function undoMultipleDefered(toUndoCount) {
|
||||
undoTimer.toUndoCount = toUndoCount;
|
||||
undoTimer.start()
|
||||
if(toUndoCount > 0)
|
||||
saved = false
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
\qmlmethod void History::redoMultipleDefered(int toRedoCount)
|
||||
Redoes several HistoryLib.Action at the top of the redo stack and pushes them to the top of the undo stack.
|
||||
It redoes them deferedly to avoid overwhelming the computer while creating a cool short accelerated summary of all changes.
|
||||
*/
|
||||
function redoMultipleDefered(toRedoCount) {
|
||||
redoTimer.toRedoCount = toRedoCount;
|
||||
redoTimer.start()
|
||||
if(toRedoCount > 0)
|
||||
saved = false
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: undoTimer
|
||||
interval: 5; running: false; repeat: true
|
||||
property int toUndoCount: 0
|
||||
onTriggered: {
|
||||
if(toUndoCount > 0) {
|
||||
historyObj.undo(toUndoCount % 4 == 1) // Only redraw once every 4 changes.
|
||||
toUndoCount--;
|
||||
} else {
|
||||
running = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: redoTimer
|
||||
interval: 5; running: false; repeat: true
|
||||
property int toRedoCount: 0
|
||||
onTriggered: {
|
||||
if(toRedoCount > 0) {
|
||||
historyObj.redo(toRedoCount % 4 == 1) // Only redraw once every 4 changes.
|
||||
toRedoCount--;
|
||||
} else {
|
||||
running = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
Modules.History.initialize({
|
||||
historyObj,
|
||||
themeTextColor: sysPalette.windowText.toString(),
|
||||
imageDepth: Screen.devicePixelRatio,
|
||||
fontSize: 14
|
||||
})
|
||||
Modules.History.redo()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick.Controls
|
||||
import QtQuick
|
||||
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
|
||||
|
@ -46,6 +48,18 @@ Item {
|
|||
true when the system is running with a dark theme, false otherwise.
|
||||
*/
|
||||
property bool darkTheme: isDarkTheme()
|
||||
|
||||
/*!
|
||||
\qmlproperty int HistoryBrowser::undoCount
|
||||
Number of actions in the undo stack.
|
||||
*/
|
||||
property int undoCount: 0
|
||||
|
||||
/*!
|
||||
\qmlproperty int HistoryBrowser::redoCount
|
||||
Number of actions in the redo stack.
|
||||
*/
|
||||
property int redoCount: 0
|
||||
|
||||
Setting.TextSetting {
|
||||
id: filterInput
|
||||
|
@ -76,19 +90,22 @@ Item {
|
|||
id: redoColumn
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
width: actionWidth
|
||||
width: historyBrowser.actionWidth
|
||||
|
||||
Repeater {
|
||||
model: history.redoCount
|
||||
model: historyBrowser.redoCount
|
||||
|
||||
HistoryItem {
|
||||
id: redoButton
|
||||
width: actionWidth
|
||||
width: historyBrowser.actionWidth
|
||||
//height: actionHeight
|
||||
isRedo: true
|
||||
idx: index
|
||||
darkTheme: historyBrowser.darkTheme
|
||||
hidden: !(filterInput.value == "" || content.includes(filterInput.value))
|
||||
onClicked: {
|
||||
redoTimer.toRedoCount = Modules.History.redoStack.length-index
|
||||
redoTimer.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -101,14 +118,14 @@ Item {
|
|||
transform: Rotation { origin.x: 30; origin.y: 30; angle: 270}
|
||||
height: 70
|
||||
width: 20
|
||||
visible: history.redoCount > 0
|
||||
visible: historyBrowser.redoCount > 0
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: nowRect
|
||||
anchors.right: parent.right
|
||||
anchors.top: redoColumn.bottom
|
||||
width: actionWidth
|
||||
width: historyBrowser.actionWidth
|
||||
height: 40
|
||||
color: sysPalette.highlight
|
||||
Text {
|
||||
|
@ -124,20 +141,24 @@ Item {
|
|||
id: undoColumn
|
||||
anchors.right: parent.right
|
||||
anchors.top: nowRect.bottom
|
||||
width: actionWidth
|
||||
width: historyBrowser.actionWidth
|
||||
|
||||
Repeater {
|
||||
model: history.undoCount
|
||||
model: historyBrowser.undoCount
|
||||
|
||||
|
||||
HistoryItem {
|
||||
id: undoButton
|
||||
width: actionWidth
|
||||
width: historyBrowser.actionWidth
|
||||
//height: actionHeight
|
||||
isRedo: false
|
||||
idx: index
|
||||
darkTheme: historyBrowser.darkTheme
|
||||
hidden: !(filterInput.value == "" || content.includes(filterInput.value))
|
||||
|
||||
onClicked: {
|
||||
undoTimer.toUndoCount = +index+1
|
||||
undoTimer.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -150,7 +171,39 @@ Item {
|
|||
transform: Rotation { origin.x: 30; origin.y: 30; angle: 270}
|
||||
height: 60
|
||||
width: 20
|
||||
visible: history.undoCount > 0
|
||||
visible: historyBrowser.undoCount > 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: undoTimer
|
||||
interval: 5; running: false; repeat: true
|
||||
property int toUndoCount: 0
|
||||
onTriggered: {
|
||||
if(toUndoCount > 0) {
|
||||
Modules.History.undo()
|
||||
if(toUndoCount % 3 === 1)
|
||||
Modules.Canvas.requestPaint()
|
||||
toUndoCount--;
|
||||
} else {
|
||||
running = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: redoTimer
|
||||
interval: 5; running: false; repeat: true
|
||||
property int toRedoCount: 0
|
||||
onTriggered: {
|
||||
if(toRedoCount > 0) {
|
||||
Modules.History.redo()
|
||||
if(toRedoCount % 3 === 1)
|
||||
Modules.Canvas.requestPaint()
|
||||
toRedoCount--;
|
||||
} else {
|
||||
running = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -163,6 +216,18 @@ Item {
|
|||
let hex = sysPalette.windowText.toString()
|
||||
// We only check the first parameter, as on all normal OSes, text color is grayscale.
|
||||
return parseInt(hex.substr(1,2), 16) > 128
|
||||
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
Modules.History.initialize({
|
||||
helper: Helper,
|
||||
themeTextColor: sysPalette.windowText.toString(),
|
||||
imageDepth: Screen.devicePixelRatio,
|
||||
fontSize: 14
|
||||
})
|
||||
Modules.History.on("updated undone redone", () => {
|
||||
undoCount = Modules.History.undoStack.length
|
||||
redoCount = Modules.History.redoStack.length
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,17 +41,17 @@ Button {
|
|||
\qmlproperty bool HistoryItem::isRedo
|
||||
true if the action is in the redo stack, false othewise.
|
||||
*/
|
||||
property bool isRedo
|
||||
required property bool isRedo
|
||||
/*!
|
||||
\qmlproperty int HistoryItem::idx
|
||||
\qmlproperty int HistoryItem::index
|
||||
Index of the item within the HistoryBrowser list.
|
||||
*/
|
||||
property int idx
|
||||
required property int index
|
||||
/*!
|
||||
\qmlproperty bool HistoryItem::darkTheme
|
||||
true when the system is running with a dark theme, false otherwise.
|
||||
*/
|
||||
property bool darkTheme
|
||||
required property bool darkTheme
|
||||
/*!
|
||||
\qmlproperty bool HistoryItem::hidden
|
||||
true when the item is filtered out, false otherwise.
|
||||
|
@ -61,7 +61,7 @@ Button {
|
|||
\qmlproperty int HistoryItem::historyAction
|
||||
Associated history action.
|
||||
*/
|
||||
readonly property var historyAction: isRedo ? history.redoStack[idx] : history.undoStack[history.undoCount-idx-1]
|
||||
readonly property var historyAction: isRedo ? Modules.History.redoStack.at(index) : Modules.History.undoStack.at(-index-1)
|
||||
|
||||
/*!
|
||||
\qmlproperty int HistoryItem::actionHeight
|
||||
|
@ -147,13 +147,6 @@ Button {
|
|||
ToolTip.visible: hovered
|
||||
ToolTip.delay: 200
|
||||
ToolTip.text: content
|
||||
|
||||
onClicked: {
|
||||
if(isRedo)
|
||||
history.redoMultipleDefered(history.redoCount-idx)
|
||||
else
|
||||
history.undoMultipleDefered(+idx+1)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue