refactor: Restructure project into sources/tests layout and dissolve PluginLLMCore

This commit is contained in:
Petr Mironychev
2026-07-14 02:50:43 +02:00
parent adef7972ed
commit 34d4f2c517
348 changed files with 809 additions and 852 deletions

View File

@@ -0,0 +1,34 @@
qt_add_library(QodeAssistUIControls STATIC)
qt_policy(SET QTP0001 NEW)
qt_policy(SET QTP0004 NEW)
qt_add_qml_module(QodeAssistUIControls
URI UIControls
VERSION 1.0
DEPENDENCIES QtQuick
QML_FILES
qml/Badge.qml
qml/QoAButton.qml
qml/QoABusyIndicator.qml
qml/QoABusyOverlay.qml
qml/QoATextSlider.qml
qml/QoAComboBox.qml
qml/FadeListItemAnimation.qml
qml/QoASeparator.qml
qml/QoAToolTip.qml
RESOURCES
icons/dropdown-arrow-light.svg
icons/dropdown-arrow-dark.svg
)
target_link_libraries(QodeAssistUIControls
PRIVATE
Qt6::Core
Qt6::Quick
)
target_include_directories(QodeAssistUIControls
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
)

View File

@@ -0,0 +1,4 @@
<svg width="12" height="8" viewBox="0 0 12 8" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6 8L0 0H12L6 8Z" fill="#FFFFFF"/>
</svg>

After

Width:  |  Height:  |  Size: 148 B

View File

@@ -0,0 +1,4 @@
<svg width="12" height="8" viewBox="0 0 12 8" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6 8L0 0H12L6 8Z" fill="#000000"/>
</svg>

After

Width:  |  Height:  |  Size: 148 B

View File

@@ -0,0 +1,39 @@
// Copyright (C) 2024-2026 Petr Mironychev
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
import QtQuick
import QtQuick.Controls
Rectangle {
id: root
property alias text: badgeText.text
property alias hovered: mouse.hovered
QoAToolTip {
visible: root.hovered && root.ToolTip.text.length > 0
text: root.ToolTip.text
delay: root.ToolTip.delay
}
implicitWidth: badgeText.implicitWidth + root.radius
implicitHeight: badgeText.implicitHeight + 6
color: palette.button
radius: root.height / 2
border.color: palette.mid
border.width: 1
Text {
id: badgeText
anchors.centerIn: parent
color: palette.buttonText
}
HoverHandler {
id: mouse
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
}
}

View File

@@ -0,0 +1,26 @@
// Copyright (C) 2025-2026 Petr Mironychev
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
import QtQuick
ParallelAnimation {
id: root
property Item targetObject: parent
NumberAnimation {
target: root.targetObject
property: "opacity"
to: 0
duration: 200
easing.type: Easing.InQuad
}
NumberAnimation {
target: root.targetObject
property: "height"
to: 0
duration: 250
easing.type: Easing.InQuad
}
}

View File

@@ -0,0 +1,76 @@
// Copyright (C) 2024-2026 Petr Mironychev
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
import QtQuick
Item {
id: root
property bool running: true
property color color: palette.highlightedText
property real lineWidth: Math.max(1.5, Math.min(width, height) * 0.13)
implicitWidth: 24
implicitHeight: 24
visible: opacity > 0
opacity: running ? 1.0 : 0.0
Behavior on opacity {
NumberAnimation { duration: 150 }
}
Canvas {
id: canvas
anchors.fill: parent
antialiasing: true
onPaint: {
const ctx = getContext("2d")
ctx.reset()
const cx = width / 2
const cy = height / 2
const lw = root.lineWidth
const radius = Math.min(width, height) / 2 - lw / 2 - 1
if (radius <= 0)
return
const segments = 90
const sweep = Math.PI * 1.5
ctx.lineCap = "butt"
ctx.lineWidth = lw
for (let i = 0; i < segments; ++i) {
const a0 = (i / segments) * sweep
const a1 = ((i + 1) / segments) * sweep + 0.012
const t = i / (segments - 1)
ctx.beginPath()
ctx.strokeStyle = Qt.rgba(root.color.r, root.color.g, root.color.b, 0.08 + 0.92 * t)
ctx.arc(cx, cy, radius, a0, a1, false)
ctx.stroke()
}
ctx.lineCap = "round"
ctx.strokeStyle = root.color
ctx.beginPath()
ctx.arc(cx, cy, radius, sweep - 0.001, sweep, false)
ctx.stroke()
}
RotationAnimator {
target: canvas
from: 0
to: 360
duration: 850
loops: Animation.Infinite
running: root.visible
}
}
onColorChanged: canvas.requestPaint()
onLineWidthChanged: canvas.requestPaint()
onWidthChanged: canvas.requestPaint()
onHeightChanged: canvas.requestPaint()
}

View File

@@ -0,0 +1,43 @@
// Copyright (C) 2024-2026 Petr Mironychev
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
import QtQuick
Rectangle {
id: root
property alias text: label.text
property bool active: false
visible: active
color: Qt.rgba(palette.window.r, palette.window.g, palette.window.b, 0.75)
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.AllButtons
hoverEnabled: true
preventStealing: true
onWheel: function(wheel) { wheel.accepted = true }
}
Column {
anchors.centerIn: parent
spacing: 10
QoABusyIndicator {
anchors.horizontalCenter: parent.horizontalCenter
running: root.active
width: 36
height: 36
}
Text {
id: label
anchors.horizontalCenter: parent.horizontalCenter
color: palette.text
font.pixelSize: 13
}
}
}

View File

@@ -0,0 +1,54 @@
// Copyright (C) 2024-2026 Petr Mironychev
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
import QtQuick
import QtQuick.Controls.Basic
Button {
id: control
property color accentColor: "transparent"
focusPolicy: Qt.NoFocus
padding: 4
icon.width: 16
icon.height: 16
contentItem.height: 20
background: Rectangle {
id: bg
readonly property bool hasAccent: control.accentColor.a > 0
implicitHeight: 20
color: !control.enabled || !control.down ? control.palette.button : control.palette.dark
border.color: bg.hasAccent
? control.accentColor
: (!control.enabled || (!control.hovered && !control.visualFocus) ? control.palette.mid : control.palette.highlight)
border.width: bg.hasAccent ? 2 : 1
radius: 4
Rectangle {
anchors.fill: bg
radius: bg.radius
gradient: Gradient {
GradientStop { position: 0.0; color: Qt.alpha(control.palette.highlight, 0.4) }
GradientStop { position: 1.0; color: Qt.alpha(control.palette.highlight, 0.2) }
}
opacity: control.hovered ? 0.3 : 0.01
Behavior on opacity {NumberAnimation{duration: 250}}
}
Rectangle {
anchors.fill: bg
radius: bg.radius
color: control.accentColor
visible: bg.hasAccent
opacity: 0.15
}
}
}

View File

@@ -0,0 +1,168 @@
// Copyright (C) 2025-2026 Petr Mironychev
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic as Basic
Basic.ComboBox {
id: control
property real popupContentWidth: 100
implicitWidth: Math.min(contentItem.implicitWidth + 8, 300)
implicitHeight: 30
function updatePopupWidth() {
var maxWidth = 100;
if (model) {
for (var i = 0; i < model.length; i++) {
textMetrics.text = model[i];
maxWidth = Math.max(maxWidth, textMetrics.width + 40);
}
}
popupContentWidth = Math.min(maxWidth, 350);
}
onModelChanged: updatePopupWidth()
Component.onCompleted: updatePopupWidth()
clip: true
indicator: Image {
id: dropdownIcon
x: control.width - width - 10
y: control.topPadding + (control.availableHeight - height) / 2
width: 12
height: 8
source: palette.window.hslLightness > 0.5
? "qrc:/qt/qml/UIControls/icons/dropdown-arrow-light.svg"
: "qrc:/qt/qml/UIControls/icons/dropdown-arrow-dark.svg"
sourceSize: Qt.size(width, height)
rotation: control.popup.visible ? 180 : 0
Behavior on rotation {
NumberAnimation { duration: 150; easing.type: Easing.OutQuad }
}
}
background: Rectangle {
id: bg
implicitWidth: control.implicitWidth
implicitHeight: 30
color: !control.enabled || !control.down ? palette.button : palette.dark
border.color: !control.enabled || (!control.hovered && !control.visualFocus)
? palette.mid
: palette.highlight
border.width: 1
radius: 5
Behavior on color {
ColorAnimation { duration: 150 }
}
Behavior on border.color {
ColorAnimation { duration: 150 }
}
Rectangle {
anchors.fill: bg
radius: bg.radius
gradient: Gradient {
GradientStop { position: 0.0; color: Qt.alpha(palette.highlight, 0.4) }
GradientStop { position: 1.0; color: Qt.alpha(palette.highlight, 0.2) }
}
opacity: control.hovered ? 0.3 : 0.01
Behavior on opacity {
NumberAnimation { duration: 250 }
}
}
}
contentItem: Text {
leftPadding: 10
rightPadding: 30
text: control.displayText
font.pixelSize: 12
color: palette.text
verticalAlignment: Text.AlignVCenter
elide: Text.ElideNone
}
popup: Popup {
y: control.height + 2
width: Math.max(control.width, control.popupContentWidth)
implicitHeight: Math.min(contentItem.implicitHeight, 300)
padding: 4
contentItem: ListView {
clip: true
implicitHeight: contentHeight
model: control.popup.visible ? control.delegateModel : null
currentIndex: control.highlightedIndex
boundsBehavior: ListView.StopAtBounds
highlightMoveDuration: 0
ScrollBar.vertical: ScrollBar {
policy: ScrollBar.AsNeeded
}
}
background: Rectangle {
color: palette.base
border.color: Qt.lighter(palette.mid, 1.1)
border.width: 1
radius: 5
}
enter: Transition {
NumberAnimation { property: "opacity"; from: 0.0; to: 1.0; duration: 150 }
}
exit: Transition {
NumberAnimation { property: "opacity"; from: 1.0; to: 0.0; duration: 100 }
}
}
delegate: ItemDelegate {
width: control.popup.width - 8
height: 32
contentItem: Text {
text: modelData
color: palette.text
font.pixelSize: 12
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
leftPadding: 10
}
highlighted: control.highlightedIndex === index
background: Rectangle {
radius: 4
color: highlighted
? Qt.alpha(palette.highlight, 0.2)
: parent.hovered
? (palette.window.hslLightness > 0.5
? Qt.darker(palette.base, 1.05)
: Qt.lighter(palette.base, 1.15))
: "transparent"
Behavior on color {
ColorAnimation { duration: 100 }
}
}
}
TextMetrics {
id: textMetrics
font.pixelSize: 12
}
}

View File

@@ -0,0 +1,13 @@
// Copyright (C) 2025-2026 Petr Mironychev
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
import QtQuick
Rectangle {
id: root
height: 15
width: 1
color: palette.mid
}

View File

@@ -0,0 +1,140 @@
// Copyright (C) 2025-2026 Petr Mironychev
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
import QtQuick
import QtQuick.Controls.Basic
Item {
id: root
property alias leftText: leftLabel.text
property alias rightText: rightLabel.text
property bool checked: false
property bool enabled: true
property bool hovered: mouseArea.containsMouse
property int padding: 8
signal toggled()
readonly property real maxTextWidth: Math.max(leftLabel.implicitWidth, rightLabel.implicitWidth) + padding * 2
readonly property real maxTextHeight: Math.max(leftLabel.implicitHeight, rightLabel.implicitHeight) + padding
implicitWidth: maxTextWidth * 2
implicitHeight: maxTextHeight
Rectangle {
anchors.fill: parent
radius: height / 2
color: root.enabled ? palette.button : palette.mid
border.color: root.enabled ? palette.mid : palette.midlight
border.width: 1
Behavior on color {
ColorAnimation { duration: 150 }
}
}
Rectangle {
id: slider
anchors.verticalCenter: parent.verticalCenter
x: root.checked ? parent.width / 2 - 1 : 1
width: parent.width / 2
height: parent.height - 2
opacity: 0.6
radius: height / 2
color: root.enabled
? (mouseArea.pressed ? palette.dark : palette.highlight)
: palette.midlight
border.color: root.enabled ? palette.highlight : palette.mid
border.width: 1
Rectangle {
anchors.fill: parent
radius: parent.radius
gradient: Gradient {
GradientStop { position: 0.0; color: Qt.alpha(palette.highlight, 0.4) }
GradientStop { position: 1.0; color: Qt.alpha(palette.highlight, 0.2) }
}
opacity: root.hovered ? 0.3 : 0.01
Behavior on opacity {
NumberAnimation { duration: 250 }
}
}
Behavior on x {
NumberAnimation {
duration: 200
easing.type: Easing.InOutQuad
}
}
Behavior on color {
ColorAnimation { duration: 150 }
}
}
Item {
id: leftContainer
width: parent.width / 2
height: parent.height
Text {
id: leftLabel
anchors.centerIn: parent
text: root.leftText
font.pixelSize: 14
color: root.enabled
? (!root.checked ? palette.highlightedText : palette.windowText)
: Qt.alpha(palette.windowText, 0.3)
Behavior on color {
ColorAnimation { duration: 150 }
}
}
}
Item {
id: rightContainer
x: parent.width / 2
width: parent.width / 2
height: parent.height
Text {
id: rightLabel
anchors.centerIn: parent
text: root.rightText
font.pixelSize: 14
color: root.enabled
? (root.checked ? palette.highlightedText : palette.windowText)
: Qt.alpha(palette.windowText, 0.3)
Behavior on color {
ColorAnimation { duration: 150 }
}
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: root.enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
onClicked: {
if (root.enabled) {
root.checked = !root.checked
root.toggled()
}
}
}
}

View File

@@ -0,0 +1,69 @@
// Copyright (C) 2026 Petr Mironychev
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
import QtQuick
import QtQuick.Controls
ToolTip {
id: root
padding: 8
contentItem: Text {
text: root.text
font: root.font
color: palette.toolTipText
wrapMode: Text.Wrap
}
background: Item {
implicitWidth: bg.implicitWidth
implicitHeight: bg.implicitHeight
Rectangle {
anchors.fill: bg
anchors.margins: -2
color: Qt.rgba(palette.shadow.r, palette.shadow.g, palette.shadow.b, 0.12)
radius: 8
z: -2
}
Rectangle {
anchors.fill: bg
anchors.margins: -1
color: Qt.rgba(palette.shadow.r, palette.shadow.g, palette.shadow.b, 0.08)
radius: 7
z: -1
}
Rectangle {
id: bg
anchors.fill: parent
color: palette.toolTipBase
border.color: Qt.darker(palette.toolTipBase, 1.2)
border.width: 1
radius: 6
}
}
enter: Transition {
NumberAnimation {
property: "opacity"
from: 0.0
to: 1.0
duration: 150
easing.type: Easing.OutQuad
}
}
exit: Transition {
NumberAnimation {
property: "opacity"
from: 1.0
to: 0.0
duration: 100
easing.type: Easing.InQuad
}
}
}