mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-24 13:54:57 +00:00
feat(qml): MD3 rework batch 3 — atoms (CPanel, CSection, CBlockquote, CHighlight, CProse, CMarkdown)
CPanel: radius 16, tonal surface container, tonal primary header CSection: 22px bold title, inline divider CBlockquote: 4px left accent border, tonal primary background, radius 8 CHighlight: tonal color variants at 15% opacity, radius 4 CProse: 16px body, 1.6 line-height, proportional height mode CMarkdown: updated typography to match MD3 body scale Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,53 +3,65 @@ import QtQuick.Layouts
|
||||
import QmlComponents 1.0
|
||||
|
||||
/**
|
||||
* CBlockquote.qml - Blockquote (mirrors _blockquote.scss)
|
||||
* Styled quote block with left border
|
||||
* CBlockquote.qml - Material Design 3 blockquote
|
||||
* Styled quote block with primary left border and tonal background
|
||||
*/
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
|
||||
property string text: ""
|
||||
property string cite: ""
|
||||
|
||||
color: Theme.mode === "dark" ? Qt.rgba(255, 255, 255, 0.03) : Qt.rgba(0, 0, 0, 0.02)
|
||||
radius: StyleVariables.radiusSm
|
||||
|
||||
|
||||
// MD3 tonal primary background
|
||||
color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.04)
|
||||
|
||||
// MD3 rounded right side, squared left (border covers left rounding)
|
||||
radius: 8
|
||||
|
||||
implicitWidth: parent ? parent.width : 400
|
||||
implicitHeight: contentCol.implicitHeight + StyleVariables.spacingMd * 2
|
||||
|
||||
// Left accent border
|
||||
implicitHeight: contentCol.implicitHeight + 32
|
||||
|
||||
// Left accent border - 4px primary with radius 2
|
||||
Rectangle {
|
||||
id: leftBorder
|
||||
width: 4
|
||||
height: parent.height
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
color: Theme.primary
|
||||
radius: 2
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
id: contentCol
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: StyleVariables.spacingMd + 4
|
||||
anchors.rightMargin: StyleVariables.spacingMd
|
||||
anchors.topMargin: StyleVariables.spacingMd
|
||||
anchors.bottomMargin: StyleVariables.spacingMd
|
||||
spacing: StyleVariables.spacingSm
|
||||
|
||||
anchors.left: leftBorder.right
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
anchors.topMargin: 16
|
||||
anchors.bottomMargin: 16
|
||||
spacing: 8
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
text: root.text
|
||||
color: Theme.onSurface
|
||||
font.pixelSize: StyleVariables.fontSizeMd
|
||||
color: Theme.text
|
||||
font.pixelSize: 16
|
||||
font.italic: true
|
||||
font.letterSpacing: 0.5
|
||||
wrapMode: Text.Wrap
|
||||
lineHeight: 1.6
|
||||
lineHeightMode: Text.ProportionalHeight
|
||||
}
|
||||
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
text: "— " + root.cite
|
||||
color: Theme.onSurfaceVariant
|
||||
font.pixelSize: StyleVariables.fontSizeSm
|
||||
color: Theme.textSecondary
|
||||
font.pixelSize: 14
|
||||
font.letterSpacing: 0.25
|
||||
visible: root.cite !== ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,48 +2,47 @@ import QtQuick
|
||||
import QmlComponents 1.0
|
||||
|
||||
/**
|
||||
* CHighlight.qml - Text highlighting (mirrors _highlight.scss)
|
||||
* Highlight text with background color
|
||||
* CHighlight.qml - Material Design 3 inline text highlight
|
||||
* Highlight text with tonal background color
|
||||
*/
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
|
||||
property alias text: label.text
|
||||
property string variant: "default" // default, success, warning, error, info
|
||||
|
||||
// Color mapping
|
||||
|
||||
// MD3 tonal color mapping
|
||||
readonly property color _bgColor: {
|
||||
switch (variant) {
|
||||
case "success": return Theme.successContainer
|
||||
case "warning": return Theme.warningContainer
|
||||
case "error": return Theme.errorContainer
|
||||
case "info": return Theme.infoContainer
|
||||
default: return Theme.mode === "dark"
|
||||
? Qt.rgba(255, 255, 0, 0.2)
|
||||
: Qt.rgba(255, 255, 0, 0.4)
|
||||
case "success": return Qt.rgba(Theme.success.r, Theme.success.g, Theme.success.b, 0.15)
|
||||
case "warning": return Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.15)
|
||||
case "error": return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.15)
|
||||
case "info": return Qt.rgba(Theme.info.r, Theme.info.g, Theme.info.b, 0.15)
|
||||
default: return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.15)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
readonly property color _textColor: {
|
||||
switch (variant) {
|
||||
case "success": return Theme.success
|
||||
case "warning": return Theme.warning
|
||||
case "error": return Theme.error
|
||||
case "info": return Theme.info
|
||||
default: return Theme.onSurface
|
||||
default: return Theme.text
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
color: _bgColor
|
||||
radius: StyleVariables.radiusSm / 2
|
||||
|
||||
implicitWidth: label.implicitWidth + StyleVariables.spacingXs * 2
|
||||
implicitHeight: label.implicitHeight + 2
|
||||
|
||||
radius: 4
|
||||
|
||||
implicitWidth: label.implicitWidth + 8
|
||||
implicitHeight: label.implicitHeight + 4
|
||||
|
||||
Text {
|
||||
id: label
|
||||
anchors.centerIn: parent
|
||||
color: root._textColor
|
||||
font.pixelSize: StyleVariables.fontSizeSm
|
||||
font.pixelSize: 14
|
||||
font.letterSpacing: 0.25
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,25 +2,27 @@ import QtQuick
|
||||
import QmlComponents 1.0
|
||||
|
||||
/**
|
||||
* CMarkdown.qml - Markdown text display (mirrors _markdown.scss)
|
||||
* Renders markdown-formatted text using Qt's built-in support
|
||||
* CMarkdown.qml - Material Design 3 markdown text display
|
||||
* Renders markdown-formatted text with MD3 typography
|
||||
*/
|
||||
Text {
|
||||
id: root
|
||||
|
||||
|
||||
property string markdown: ""
|
||||
|
||||
|
||||
text: markdown
|
||||
textFormat: Text.MarkdownText
|
||||
color: Theme.onSurface
|
||||
font.pixelSize: StyleVariables.fontSizeMd
|
||||
color: Theme.text
|
||||
font.pixelSize: 16
|
||||
font.letterSpacing: 0.5
|
||||
wrapMode: Text.Wrap
|
||||
lineHeight: 1.6
|
||||
|
||||
// Link styling
|
||||
lineHeightMode: Text.ProportionalHeight
|
||||
|
||||
// MD3 primary link color
|
||||
linkColor: Theme.primary
|
||||
onLinkActivated: (link) => Qt.openUrlExternally(link)
|
||||
|
||||
|
||||
// Cursor for links
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
|
||||
@@ -4,22 +4,22 @@ import QtQuick.Effects
|
||||
import QmlComponents 1.0
|
||||
|
||||
/**
|
||||
* CPanel.qml - Panel component (mirrors _panel.scss)
|
||||
* CPanel.qml - Material Design 3 surface container panel
|
||||
* Floating panel with header, body, and footer sections
|
||||
*
|
||||
*
|
||||
* Usage:
|
||||
* CPanel {
|
||||
* title: "Queue Status"
|
||||
* icon: "📋"
|
||||
* collapsible: true
|
||||
*
|
||||
*
|
||||
* // Content goes in body
|
||||
* Text { text: "Panel content" }
|
||||
* }
|
||||
*/
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
|
||||
// Public properties
|
||||
property string title: ""
|
||||
property string icon: ""
|
||||
@@ -28,57 +28,60 @@ Rectangle {
|
||||
property bool collapsible: false
|
||||
property bool collapsed: false
|
||||
property string footer: ""
|
||||
|
||||
|
||||
// Content slot
|
||||
default property alias content: bodyContent.data
|
||||
|
||||
|
||||
// Signals
|
||||
signal headerClicked()
|
||||
|
||||
|
||||
// Size
|
||||
implicitWidth: 280
|
||||
implicitHeight: collapsed ? headerRect.height : (headerRect.height + bodyLoader.height + footerLoader.height)
|
||||
|
||||
|
||||
// Positioning
|
||||
anchors.right: position === "fixed-br" || position === "fixed-tr" ? parent.right : undefined
|
||||
anchors.left: position === "fixed-bl" || position === "fixed-tl" ? parent.left : undefined
|
||||
anchors.bottom: position === "fixed-br" || position === "fixed-bl" ? parent.bottom : undefined
|
||||
anchors.top: position === "fixed-tr" || position === "fixed-tl" ? parent.top : undefined
|
||||
anchors.margins: position !== "none" ? StyleVariables.spacingMd : 0
|
||||
|
||||
// Appearance
|
||||
color: Theme.paper
|
||||
radius: StyleVariables.radiusMd
|
||||
|
||||
// Shadow for elevated variant
|
||||
anchors.margins: position !== "none" ? 16 : 0
|
||||
|
||||
// MD3 Surface container
|
||||
color: Theme.mode === "dark" ? Qt.rgba(1, 1, 1, 0.08) : Qt.rgba(0.31, 0.31, 0.44, 0.10)
|
||||
radius: 16
|
||||
border.width: 1
|
||||
border.color: Theme.mode === "dark" ? Qt.rgba(1, 1, 1, 0.12) : Qt.rgba(0, 0, 0, 0.12)
|
||||
|
||||
// MD3 elevation shadow for elevated variant
|
||||
layer.enabled: variant === "elevated"
|
||||
layer.effect: MultiEffect {
|
||||
shadowEnabled: true
|
||||
shadowColor: "#40000000"
|
||||
shadowBlur: 0.4
|
||||
shadowVerticalOffset: 6
|
||||
shadowColor: "#30000000"
|
||||
shadowBlur: 0.6
|
||||
shadowVerticalOffset: 4
|
||||
shadowHorizontalOffset: 0
|
||||
}
|
||||
|
||||
|
||||
// Smooth collapse animation
|
||||
Behavior on implicitHeight {
|
||||
NumberAnimation { duration: StyleVariables.transitionNormal; easing.type: Easing.OutCubic }
|
||||
NumberAnimation { duration: 250; easing.type: Easing.OutCubic }
|
||||
}
|
||||
|
||||
|
||||
// Layout
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
|
||||
// Header
|
||||
Rectangle {
|
||||
id: headerRect
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 40
|
||||
color: Qt.darker(Theme.primary, 1.2)
|
||||
Layout.preferredHeight: 48
|
||||
color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12)
|
||||
radius: root.radius
|
||||
visible: root.title || root.icon
|
||||
|
||||
// Square off bottom corners
|
||||
|
||||
// Square off bottom corners when not collapsed
|
||||
Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
@@ -87,7 +90,7 @@ Rectangle {
|
||||
color: parent.color
|
||||
visible: !root.collapsed
|
||||
}
|
||||
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: root.collapsible ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
@@ -98,98 +101,104 @@ Rectangle {
|
||||
root.headerClicked()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: StyleVariables.spacingMd
|
||||
anchors.rightMargin: StyleVariables.spacingMd
|
||||
spacing: StyleVariables.spacingSm
|
||||
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
spacing: 12
|
||||
|
||||
// Icon
|
||||
Text {
|
||||
visible: root.icon
|
||||
text: root.icon
|
||||
font.pixelSize: StyleVariables.fontSizeMd
|
||||
color: "#ffffff"
|
||||
font.pixelSize: 16
|
||||
color: Theme.primary
|
||||
}
|
||||
|
||||
|
||||
// Title
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
text: root.title
|
||||
font.pixelSize: StyleVariables.fontSizeSm
|
||||
font.pixelSize: 14
|
||||
font.weight: Font.Medium
|
||||
color: "#ffffff"
|
||||
font.letterSpacing: 0.1
|
||||
color: Theme.text
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
|
||||
// Collapse indicator
|
||||
Text {
|
||||
visible: root.collapsible
|
||||
text: root.collapsed ? "▼" : "▲"
|
||||
font.pixelSize: 10
|
||||
color: "#cccccc"
|
||||
color: Theme.textSecondary
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Body
|
||||
Loader {
|
||||
id: bodyLoader
|
||||
Layout.fillWidth: true
|
||||
active: !root.collapsed
|
||||
visible: active
|
||||
|
||||
|
||||
sourceComponent: Rectangle {
|
||||
width: bodyLoader.width
|
||||
height: Math.min(bodyContent.implicitHeight, 300)
|
||||
height: Math.min(bodyContent.implicitHeight + 32, 332)
|
||||
color: "transparent"
|
||||
clip: true
|
||||
|
||||
|
||||
Flickable {
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: 16
|
||||
anchors.bottomMargin: 16
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
contentHeight: bodyContent.implicitHeight
|
||||
clip: true
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
id: bodyContent
|
||||
width: parent.width
|
||||
spacing: StyleVariables.spacingSm
|
||||
spacing: 8
|
||||
}
|
||||
|
||||
|
||||
ScrollBar.vertical: ScrollBar {
|
||||
policy: bodyContent.implicitHeight > 300 ? ScrollBar.AsNeeded : ScrollBar.AlwaysOff
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Footer
|
||||
Loader {
|
||||
id: footerLoader
|
||||
Layout.fillWidth: true
|
||||
active: root.footer && !root.collapsed
|
||||
visible: active
|
||||
|
||||
|
||||
sourceComponent: Rectangle {
|
||||
width: footerLoader.width
|
||||
height: 32
|
||||
height: 40
|
||||
color: "transparent"
|
||||
|
||||
|
||||
// Top border
|
||||
Rectangle {
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: 1
|
||||
color: Theme.divider
|
||||
color: Theme.mode === "dark" ? Qt.rgba(1, 1, 1, 0.12) : Qt.rgba(0, 0, 0, 0.12)
|
||||
}
|
||||
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: root.footer
|
||||
font.pixelSize: StyleVariables.fontSizeXs
|
||||
font.pixelSize: 12
|
||||
font.letterSpacing: 0.4
|
||||
color: Theme.textSecondary
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,19 +3,19 @@ import QtQuick.Layouts
|
||||
import QmlComponents 1.0
|
||||
|
||||
/**
|
||||
* CProse.qml - Long-form text container (mirrors _prose.scss)
|
||||
* Optimized typography for reading
|
||||
* CProse.qml - Material Design 3 long-form text container
|
||||
* Rich text container with proper line-height and typography
|
||||
*/
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
|
||||
property string size: "md" // sm, md, lg
|
||||
property real maxWidth: 680 // Optimal reading width
|
||||
|
||||
|
||||
default property alias content: contentItem.data
|
||||
|
||||
spacing: StyleVariables.spacingMd
|
||||
|
||||
|
||||
spacing: 16
|
||||
|
||||
// Content container with max-width
|
||||
Item {
|
||||
id: contentItem
|
||||
@@ -23,8 +23,8 @@ ColumnLayout {
|
||||
Layout.maximumWidth: root.maxWidth
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
implicitHeight: childrenRect.height
|
||||
|
||||
// Apply prose styles to child Text elements
|
||||
|
||||
// Apply MD3 prose styles to child Text elements
|
||||
Component.onCompleted: {
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
var child = children[i]
|
||||
@@ -34,21 +34,23 @@ ColumnLayout {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function applyProseStyle(textItem) {
|
||||
textItem.color = Theme.onSurface
|
||||
textItem.color = Theme.text
|
||||
textItem.wrapMode = Text.Wrap
|
||||
textItem.lineHeight = 1.7
|
||||
|
||||
textItem.lineHeight = 1.6
|
||||
textItem.lineHeightMode = Text.ProportionalHeight
|
||||
textItem.font.letterSpacing = 0.5
|
||||
|
||||
switch (size) {
|
||||
case "sm":
|
||||
textItem.font.pixelSize = StyleVariables.fontSizeSm
|
||||
textItem.font.pixelSize = 14
|
||||
break
|
||||
case "lg":
|
||||
textItem.font.pixelSize = StyleVariables.fontSizeLg
|
||||
textItem.font.pixelSize = 18
|
||||
break
|
||||
default:
|
||||
textItem.font.pixelSize = StyleVariables.fontSizeMd
|
||||
textItem.font.pixelSize = 16
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,58 +3,66 @@ import QtQuick.Layouts
|
||||
import QmlComponents 1.0
|
||||
|
||||
/**
|
||||
* CSection.qml - Content section (mirrors _section.scss)
|
||||
* Groups related content with optional title
|
||||
* CSection.qml - Material Design 3 content section
|
||||
* Groups related content with optional title and subtitle
|
||||
*/
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
|
||||
property string title: ""
|
||||
property string subtitle: ""
|
||||
property bool divider: false
|
||||
property string spacing: "md" // sm, md, lg
|
||||
|
||||
|
||||
default property alias content: contentItem.data
|
||||
|
||||
|
||||
spacing: {
|
||||
switch (root.spacing) {
|
||||
case "sm": return StyleVariables.spacingSm
|
||||
case "lg": return StyleVariables.spacingLg
|
||||
default: return StyleVariables.spacingMd
|
||||
case "sm": return 8
|
||||
case "lg": return 24
|
||||
default: return 16
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Header
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: StyleVariables.spacingXs
|
||||
spacing: 4
|
||||
visible: root.title !== "" || root.subtitle !== ""
|
||||
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
text: root.title
|
||||
color: Theme.onSurface
|
||||
font.pixelSize: StyleVariables.fontSizeLg
|
||||
font.weight: Font.DemiBold
|
||||
color: Theme.text
|
||||
font.pixelSize: 22
|
||||
font.weight: Font.Bold
|
||||
font.letterSpacing: 0
|
||||
lineHeight: 1.27
|
||||
lineHeightMode: Text.ProportionalHeight
|
||||
visible: root.title !== ""
|
||||
}
|
||||
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
text: root.subtitle
|
||||
color: Theme.onSurfaceVariant
|
||||
font.pixelSize: StyleVariables.fontSizeSm
|
||||
color: Theme.textSecondary
|
||||
font.pixelSize: 14
|
||||
font.letterSpacing: 0.25
|
||||
lineHeight: 1.43
|
||||
lineHeightMode: Text.ProportionalHeight
|
||||
wrapMode: Text.Wrap
|
||||
visible: root.subtitle !== ""
|
||||
}
|
||||
}
|
||||
|
||||
// Divider after header
|
||||
CDivider {
|
||||
|
||||
// Bottom divider after header
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
height: 1
|
||||
color: Theme.mode === "dark" ? Qt.rgba(1, 1, 1, 0.12) : Qt.rgba(0, 0, 0, 0.12)
|
||||
visible: root.divider && root.title !== ""
|
||||
}
|
||||
|
||||
|
||||
// Content
|
||||
Item {
|
||||
id: contentItem
|
||||
|
||||
Reference in New Issue
Block a user