// Copyright (C) 2024-2026 Petr Mironychev // SPDX-License-Identifier: GPL-3.0-or-later import QtQuick import QtQuick.Controls import ChatView import UIControls import Qt.labs.platform as Platform Rectangle { id: root property string code: "" property string language: "" property bool expanded: false property Flickable viewport: null property alias codeFontFamily: codeText.font.family property alias codeFontSize: codeText.font.pointSize readonly property real collapsedHeight: copyButton.height + 10 color: palette.alternateBase border.color: root.color.hslLightness > 0.5 ? Qt.darker(root.color, 1.3) : Qt.lighter(root.color, 1.3) border.width: 2 radius: 4 implicitWidth: parent.width clip: true Behavior on implicitHeight { NumberAnimation { duration: 200; easing.type: Easing.InOutQuad } } ChatUtils { id: utils } HoverHandler { id: hoverHandler enabled: true } MouseArea { id: header width: parent.width height: root.collapsedHeight cursorShape: Qt.PointingHandCursor onClicked: root.expanded = !root.expanded Row { id: headerRow anchors { verticalCenter: parent.verticalCenter left: parent.left leftMargin: 10 } spacing: 6 Text { text: root.language ? qsTr("Code (%1)").arg(root.language) : qsTr("Code") font.pixelSize: 12 font.bold: true color: palette.text } Text { anchors.verticalCenter: parent.verticalCenter text: root.expanded ? "▼" : "▶" font.pixelSize: 10 color: palette.mid } } } TextEdit { id: codeText anchors { left: parent.left right: parent.right top: header.bottom margins: 10 } text: root.code readOnly: true selectByMouse: true color: parent.color.hslLightness > 0.5 ? "black" : "white" wrapMode: Text.WordWrap selectionColor: palette.highlight MouseArea { anchors.fill: parent acceptedButtons: Qt.RightButton onClicked: contextMenu.open() } } Platform.Menu { id: contextMenu Platform.MenuItem { text: qsTr("Copy") onTriggered: { const textToCopy = codeText.selectedText || root.code utils.copyToClipboard(textToCopy) } } Platform.MenuSeparator {} Platform.MenuItem { text: root.expanded ? qsTr("Collapse") : qsTr("Expand") onTriggered: root.expanded = !root.expanded } } QoAButton { id: copyButton anchors.right: parent.right anchors.rightMargin: 5 y: { if (!root.expanded || !root.viewport) return 5 const flick = root.viewport const topInContent = root.mapToItem(flick.contentItem, 0, 0).y const topInView = topInContent - flick.contentY const desired = topInView < 0 ? (-topInView + 5) : 5 const maxY = Math.max(5, root.height - copyButton.height - 5) return Math.max(5, Math.min(desired, maxY)) } text: qsTr("Copy") onClicked: { utils.copyToClipboard(root.code) text = qsTr("Copied") copyTimer.start() } Timer { id: copyTimer interval: 2000 onTriggered: parent.text = qsTr("Copy") } } states: [ State { when: !root.expanded PropertyChanges { target: root implicitHeight: root.collapsedHeight } }, State { when: root.expanded PropertyChanges { target: root implicitHeight: header.height + codeText.implicitHeight + 10 } } ] }