DPS_Manage/DelegateUI/Controls/DelTimeline.qml

308 lines
10 KiB
QML

import QtQuick 2.15
import QtQuick.Templates 2.15 as T
import DelegateUI 1.0
Item {
id: control
enum Mode {
Mode_Left = 0,
Mode_Right = 1,
Mode_Alternate = 2
}
timeFont {
family: DelTheme.DelTimeline.fontFamily
pixelSize: DelTheme.DelTimeline.fontSize
}
contentFont {
family: DelTheme.DelTimeline.fontFamily
pixelSize: DelTheme.DelTimeline.fontSize
}
property bool animationEnabled: DelTheme.animationEnabled
property var initModel: []
property int mode: DelTimeline.Mode_Left
property bool reverse: false
property int defaultNodeSize: 11
property int defaultLineWidth: 1
property string defaultTimeFormat: "yyyy-MM-dd"
property int defaultContentFormat: Text.AutoText
property color colorNode: DelTheme.DelTimeline.colorNode
property color colorNodeBg: DelTheme.DelTimeline.colorNodeBg
property color colorLine: DelTheme.DelTimeline.colorLine
property font timeFont
property color colorTimeText: DelTheme.DelTimeline.colorTimeText
property font contentFont
property color colorContentText: DelTheme.DelTimeline.colorContentText
property Component nodeDelegate: Component {
Item {
height: __loading.active ? __loading.height : __icon.active ? __icon.height : defaultNodeSize
Loader {
id: __dot
width: parent.height
height: parent.height
anchors.horizontalCenter: parent.horizontalCenter
active: !__icon.active && !__loading.active
sourceComponent: Rectangle {
radius: width >> 1
color: control.colorNodeBg
border.color: model.colorNode
border.width: radius * 0.5
}
}
Loader {
id: __icon
anchors.horizontalCenter: parent.horizontalCenter
active: !__loading.active && model.icon !== 0
sourceComponent: DelIconText {
iconSource: model.icon
iconSize: model.iconSize
colorIcon: model.colorNode
}
}
Loader {
id: __loading
anchors.horizontalCenter: parent.horizontalCenter
active: model.loading
sourceComponent: DelIconText {
iconSize: model.iconSize
iconSource: DelIcon.LoadingOutlined
colorIcon: model.colorNode
NumberAnimation on rotation {
running: model.loading
from: 0
to: 360
loops: Animation.Infinite
duration: 1000
}
}
}
}
}
property Component lineDelegate: Component {
Rectangle {
color: control.colorLine
}
}
property Component timeDelegate: Component {
Text {
id: __timeText
color: control.colorTimeText
font: control.timeFont
text: {
if (!isNaN(model.time))
return model.time.toLocaleString(Qt.locale(), model.timeFormat);
else
return "";
}
horizontalAlignment: onLeft ? Text.AlignRight : Text.AlignLeft
}
}
property Component contentDelegate: Component {
Text {
id: __contentText
color: control.colorContentText
font: control.contentFont
text: model.content
textFormat: model.contentFormat
wrapMode: Text.WrapAnywhere
horizontalAlignment: onLeft ? Text.AlignRight : Text.AlignLeft
}
}
onInitModelChanged: {
clear();
for (const object of initModel) {
append(object);
}
}
function flick(xVelocity: real, yVelocity: real) {
__listView.flick(xVelocity, yVelocity);
}
function positionViewAtBeginning() {
__listView.positionViewAtBeginning();
}
function positionViewAtIndex(index: int, mode: int) {
__listView.positionViewAtIndex(index, mode);
}
function positionViewAtEnd() {
__listView.positionViewAtEnd();
}
function get(index) {
return __listModel.get(index);
}
function set(index, object) {
__listModel.set(index, __private.initObject(object));
}
function setProperty(index, propertyName, value) {
if (propertyName === "time")
__private.noTime = false;
__listModel.setProperty(index, propertyName, value);
}
function move(from, to, count = 1) {
__listModel.move(from, to, count);
}
function insert(index, object) {
__listModel.insert(index, __private.initObject(object));
}
function remove(index, count = 1) {
__listModel.remove(index, count);
for (let i = 0; i < __listModel.count; i++) {
if (__listModel.get(i).hasOwnProperty("time")) {
__private.noTime = false;
break;
}
}
}
function append(object) {
__listModel.append(__private.initObject(object));
}
function clear() {
__private.noTime = true;
__listModel.clear();
}
QtObject {
id: __private
property bool noTime: true
function initObject(object) {
/*! 静态角色类型下会有颜色不兼容问题, 统一转换为string即可 */
if (object.hasOwnProperty("colorNode")) {
object.colorNode = String(object.colorNode);
}
if (!object.hasOwnProperty("colorNode")) object.colorNode = String(control.colorNode);
if (!object.hasOwnProperty("icon")) object.icon = 0;
if (!object.hasOwnProperty("iconSize")) object.iconSize = control.defaultNodeSize;
if (!object.hasOwnProperty("loading")) object.loading = false;
if (!object.hasOwnProperty("time")) object.time = new Date(undefined);
if (!object.hasOwnProperty("timeFormat")) object.timeFormat = control.defaultTimeFormat;
if (!object.hasOwnProperty("content")) object.content = "";
if (!object.hasOwnProperty("contentFormat")) object.contentFormat = control.defaultContentFormat;
/*! 判断是否存在有效时间 */
if (__private.noTime && object.hasOwnProperty("time") && !isNaN(object.time))
__private.noTime = false;
return object;
}
}
ListView {
id: __listView
anchors.fill: parent
clip: true
verticalLayoutDirection: control.reverse ? ListView.BottomToTop : ListView.TopToBottom
model: ListModel { id: __listModel }
T.ScrollBar.vertical: DelScrollBar { }
add: Transition {
NumberAnimation { property: "opacity"; from: 0; to: 1; duration: control.animationEnabled ? DelTheme.Primary.durationMid : 0 }
}
remove: Transition {
NumberAnimation { property: "opacity"; from: 1; to: 0; duration: control.animationEnabled ? DelTheme.Primary.durationMid : 0 }
}
delegate: Item {
id: __rootItem
width: __listView.width
height: contentLoader.height + 25
required property var model
required property int index
property bool timeOnLeft: {
if (control.mode == DelTimeline.Mode_Right)
return false;
else if (control.mode == DelTimeline.Mode_Alternate)
return index % 2 == 0;
else
return true;
}
Loader {
id: lineLoader
active: {
if (control.reverse)
return __rootItem.index != 0;
else
__rootItem.index !== (__listModel.count - 1);
}
width: defaultLineWidth
height: parent.height - nodeLoader.height
anchors.horizontalCenter: nodeLoader.horizontalCenter
anchors.top: nodeLoader.bottom
sourceComponent: lineDelegate
property alias model: __rootItem.model
property alias index: __rootItem.index
}
Loader {
id: nodeLoader
x: {
if (__private.noTime && control.mode != DelTimeline.Mode_Alternate)
return control.mode == DelTimeline.Mode_Left ? 0 : parent.width - width;
else
return (__rootItem.width - width) * 0.5;
}
width: 30
sourceComponent: nodeDelegate
property alias model: __rootItem.model
property alias index: __rootItem.index
}
Loader {
id: timeLoader
y: (nodeLoader.height - __timeFontMetrics.height) * 0.5
anchors.left: __rootItem.timeOnLeft ? parent.left : nodeLoader.right
anchors.leftMargin: __rootItem.timeOnLeft ? 0 : 5
anchors.right: __rootItem.timeOnLeft ? nodeLoader.left : parent.right
anchors.rightMargin: __rootItem.timeOnLeft ? 5 : 0
sourceComponent: timeDelegate
property alias model: __rootItem.model
property alias index: __rootItem.index
property bool onLeft: __rootItem.timeOnLeft
FontMetrics {
id: __timeFontMetrics
font: control.timeFont
}
}
Loader {
id: contentLoader
y: (nodeLoader.height - __contentFontMetrics.height) * 0.5
anchors.left: !__rootItem.timeOnLeft ? parent.left : nodeLoader.right
anchors.leftMargin: !__rootItem.timeOnLeft ? 0 : 5
anchors.right: !__rootItem.timeOnLeft ? nodeLoader.left : parent.right
anchors.rightMargin: !__rootItem.timeOnLeft ? 5 : 0
sourceComponent: contentDelegate
property alias model: __rootItem.model
property alias index: __rootItem.index
property bool onLeft: !__rootItem.timeOnLeft
FontMetrics {
id: __contentFontMetrics
font: control.contentFont
}
}
}
}
}