From c50691edec3f752cad2b9b7c5ca0e9e935edaa98 Mon Sep 17 00:00:00 2001
From: r4bbit <445106+0x-r4bbit@users.noreply.github.com>
Date: Tue, 28 Apr 2026 14:41:54 +0200
Subject: [PATCH] feat(amm-ui): add navbar to switch between views
---
amm-ui/qml/Main.qml | 263 +++++++++++++++++++++++-------------------
amm-ui/qml/NavBar.qml | 86 ++++++++++++++
2 files changed, 228 insertions(+), 121 deletions(-)
create mode 100644 amm-ui/qml/NavBar.qml
diff --git a/amm-ui/qml/Main.qml b/amm-ui/qml/Main.qml
index 52fa1ba..3ec1ce9 100644
--- a/amm-ui/qml/Main.qml
+++ b/amm-ui/qml/Main.qml
@@ -14,139 +14,160 @@ Item {
{ symbol: "TOK6", name: "Token 6", color: "#9b59b6", letter: "L", address: "0x1337000000000000000000000000000000000cafe", usdPrice: 0.42 }
]
- // ── Theme ─────────────────────────────────────────────────────────────────
- QtObject {
- id: theme
- property bool isDark: false
- property var colors: isDark ? dark : light
-
- readonly property var light: ({
- background: "#f7f7f5",
- cardBg: "#ffffff",
- inputBg: "#f0f0ee",
- panelBg: "#e8e8e4",
- panelHoverBg: "#ddddd8",
- textPrimary: "#111111",
- textSecondary: "#777770",
- textPlaceholder: "#bbbbb5",
- border: Qt.rgba(0,0,0,0.08),
- borderStrong: Qt.rgba(0,0,0,0.10),
- divider: Qt.rgba(0,0,0,0.06),
- ctaBg: "#111111",
- ctaHoverBg: "#2a2a28",
- selection: "#b5c4a5",
- noTokenCircle: "#c8c8c4",
- orb1: "#7a8c6a",
- orb2: "#b5c4a5",
- orb3: "#7a8c6a",
- orb4: "#c8d4b8"
- })
-
- readonly property var dark: ({
- background: "#0d0d12",
- cardBg: "#1a1a22",
- inputBg: "#222230",
- panelBg: "#2a2a38",
- panelHoverBg: "#363650",
- textPrimary: "#ffffff",
- textSecondary: "#888899",
- textPlaceholder: "#444455",
- border: Qt.rgba(1,1,1,0.08),
- borderStrong: Qt.rgba(1,1,1,0.10),
- divider: Qt.rgba(1,1,1,0.06),
- ctaBg: "#2d1530",
- ctaHoverBg: "#3d1f40",
- selection: "#4c1d4b",
- noTokenCircle: "#444455",
- orb1: "#627eea",
- orb2: "#9b59b6",
- orb3: "#fc72ff",
- orb4: "#26a17b"
- })
+ // ── Navigation bar ────────────────────────────────────────────────────────
+ // Pinned to the top; self-contained styling, unaffected by view themes.
+ NavBar {
+ id: navbar
+ anchors.top: parent.top
+ anchors.left: parent.left
+ anchors.right: parent.right
+ z: 100
}
- // ── Root background ───────────────────────────────────────────────────────
- Rectangle {
- anchors.fill: parent
- color: theme.colors.background
- Behavior on color { ColorAnimation { duration: 300 } }
-
- // Theme toggle
- Rectangle {
- anchors.top: parent.top
- anchors.right: parent.right
- anchors.margins: 16
- width: 44; height: 24; radius: 12
- color: theme.colors.panelBg
- border.color: theme.colors.border
- border.width: 1
- Text {
- anchors.centerIn: parent
- text: theme.isDark ? "☀" : "☾"
- font.pixelSize: 13
- color: theme.colors.textSecondary
- }
- MouseArea {
- anchors.fill: parent
- cursorShape: Qt.PointingHandCursor
- onClicked: theme.isDark = !theme.isDark
- }
- }
+ // ── Content area (below the nav bar) ──────────────────────────────────────
+ Item {
+ anchors.top: navbar.bottom
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
- // Decorative orbs
- Rectangle { x: -180; y: -120; width: 560; height: 560; radius: 280; color: theme.colors.orb1; opacity: 0.07 }
- Rectangle { x: parent.width - 280; y: parent.height - 320; width: 480; height: 480; radius: 240; color: theme.colors.orb2; opacity: 0.09 }
- Rectangle { x: parent.width - 200; y: -80; width: 380; height: 380; radius: 190; color: theme.colors.orb3; opacity: 0.05 }
- Rectangle { x: 40; y: parent.height - 260; width: 320; height: 320; radius: 160; color: theme.colors.orb4; opacity: 0.08 }
-
- ColumnLayout {
- anchors.centerIn: parent
- spacing: 28
-
- Text {
- Layout.alignment: Qt.AlignHCenter
- text: "Logos AMM"
- color: theme.colors.textPrimary
- font.pixelSize: 48
- font.weight: Font.Bold
+ // ── Trade view ────────────────────────────────────────────────────────
+ Item {
+ anchors.fill: parent
+ visible: navbar.currentIndex === 0
+
+ // Trade view theme — scoped here, invisible to NavBar and LP view.
+ QtObject {
+ id: theme
+ property bool isDark: false
+ property var colors: isDark ? dark : light
+
+ readonly property var light: ({
+ background: "#f7f7f5",
+ cardBg: "#ffffff",
+ inputBg: "#f0f0ee",
+ panelBg: "#e8e8e4",
+ panelHoverBg: "#ddddd8",
+ textPrimary: "#111111",
+ textSecondary: "#777770",
+ textPlaceholder: "#bbbbb5",
+ border: Qt.rgba(0,0,0,0.08),
+ borderStrong: Qt.rgba(0,0,0,0.10),
+ divider: Qt.rgba(0,0,0,0.06),
+ ctaBg: "#111111",
+ ctaHoverBg: "#2a2a28",
+ selection: "#b5c4a5",
+ noTokenCircle: "#c8c8c4",
+ orb1: "#7a8c6a",
+ orb2: "#b5c4a5",
+ orb3: "#7a8c6a",
+ orb4: "#c8d4b8"
+ })
+
+ readonly property var dark: ({
+ background: "#0d0d12",
+ cardBg: "#1a1a22",
+ inputBg: "#222230",
+ panelBg: "#2a2a38",
+ panelHoverBg: "#363650",
+ textPrimary: "#ffffff",
+ textSecondary: "#888899",
+ textPlaceholder: "#444455",
+ border: Qt.rgba(1,1,1,0.08),
+ borderStrong: Qt.rgba(1,1,1,0.10),
+ divider: Qt.rgba(1,1,1,0.06),
+ ctaBg: "#2d1530",
+ ctaHoverBg: "#3d1f40",
+ selection: "#4c1d4b",
+ noTokenCircle: "#444455",
+ orb1: "#627eea",
+ orb2: "#9b59b6",
+ orb3: "#fc72ff",
+ orb4: "#26a17b"
+ })
}
- SwapCard {
- id: swapCard
- Layout.alignment: Qt.AlignHCenter
- theme: theme
- tokens: root.tokenData
- width: Math.min(480, root.width - 32)
+ Rectangle {
+ anchors.fill: parent
+ color: theme.colors.background
+ Behavior on color { ColorAnimation { duration: 300 } }
+
+ // Theme toggle
+ Rectangle {
+ anchors.top: parent.top
+ anchors.right: parent.right
+ anchors.margins: 16
+ width: 44; height: 24; radius: 12
+ color: theme.colors.panelBg
+ border.color: theme.colors.border
+ border.width: 1
+ Text {
+ anchors.centerIn: parent
+ text: theme.isDark ? "☀" : "☾"
+ font.pixelSize: 13
+ color: theme.colors.textSecondary
+ }
+ MouseArea {
+ anchors.fill: parent
+ cursorShape: Qt.PointingHandCursor
+ onClicked: theme.isDark = !theme.isDark
+ }
+ }
- onRequestTokenSelect: function(side) {
- tokenModal.targetSide = side
- tokenModal.open()
+ // Decorative orbs
+ Rectangle { x: -180; y: -120; width: 560; height: 560; radius: 280; color: theme.colors.orb1; opacity: 0.07 }
+ Rectangle { x: parent.width - 280; y: parent.height - 320; width: 480; height: 480; radius: 240; color: theme.colors.orb2; opacity: 0.09 }
+ Rectangle { x: parent.width - 200; y: -80; width: 380; height: 380; radius: 190; color: theme.colors.orb3; opacity: 0.05 }
+ Rectangle { x: 40; y: parent.height - 260; width: 320; height: 320; radius: 160; color: theme.colors.orb4; opacity: 0.08 }
+
+ ColumnLayout {
+ anchors.centerIn: parent
+ spacing: 28
+
+ SwapCard {
+ id: swapCard
+ Layout.alignment: Qt.AlignHCenter
+ theme: theme
+ tokens: root.tokenData
+ width: Math.min(480, root.width - 32)
+
+ onRequestTokenSelect: function(side) {
+ tokenModal.targetSide = side
+ tokenModal.open()
+ }
+ }
+
+ Text {
+ Layout.alignment: Qt.AlignHCenter
+ text: "Buy and sell crypto on LEZ."
+ textFormat: Text.RichText
+ color: theme.colors.textSecondary
+ font.pixelSize: 15
+ horizontalAlignment: Text.AlignHCenter
+ }
}
- }
- Text {
- Layout.alignment: Qt.AlignHCenter
- text: "Buy and sell crypto on LEZ."
- textFormat: Text.RichText
- color: theme.colors.textSecondary
- font.pixelSize: 15
- horizontalAlignment: Text.AlignHCenter
+ TokenSelectorModal {
+ id: tokenModal
+ anchors.fill: parent
+ z: 10
+ theme: theme
+ tokens: root.tokenData
+
+ property string targetSide: "sell"
+
+ onTokenSelected: function(tok) {
+ swapCard.setToken(targetSide, tok)
+ tokenModal.close()
+ }
+ }
}
}
- TokenSelectorModal {
- id: tokenModal
+ // ── Liquidity view (placeholder — replaced when LP branch merges) ─────
+ Item {
anchors.fill: parent
- z: 10
- theme: theme
- tokens: root.tokenData
-
- property string targetSide: "sell"
-
- onTokenSelected: function(tok) {
- swapCard.setToken(targetSide, tok)
- tokenModal.close()
- }
+ visible: navbar.currentIndex === 1
}
}
}
diff --git a/amm-ui/qml/NavBar.qml b/amm-ui/qml/NavBar.qml
new file mode 100644
index 0000000..4d8a105
--- /dev/null
+++ b/amm-ui/qml/NavBar.qml
@@ -0,0 +1,86 @@
+import QtQuick 2.15
+import QtQuick.Layouts 1.15
+
+// Self-contained navigation bar — styling is independent of any view's theme.
+// Use currentIndex to read the active tab; tabChanged(index) fires on selection.
+Item {
+ id: root
+
+ property int currentIndex: 0
+ readonly property var tabs: ["Trade", "Liquidity"]
+
+ signal tabChanged(int index)
+
+ implicitHeight: 56
+
+ Rectangle {
+ anchors.fill: parent
+ color: "#ffffff"
+
+ // Bottom separator
+ Rectangle {
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ height: 1
+ color: Qt.rgba(0, 0, 0, 0.08)
+ }
+
+ RowLayout {
+ anchors.fill: parent
+ anchors.leftMargin: 20
+ anchors.rightMargin: 20
+ spacing: 4
+
+ // App identity
+ Text {
+ text: "Logos AMM"
+ color: "#111111"
+ font.pixelSize: 17
+ font.weight: Font.Bold
+ }
+
+ Item { Layout.fillWidth: true }
+
+ // Tab pills
+ Row {
+ spacing: 4
+
+ Repeater {
+ model: root.tabs
+
+ delegate: Rectangle {
+ readonly property bool active: root.currentIndex === index
+
+ height: 36
+ width: tabLabel.implicitWidth + 28
+ radius: 18
+ color: active ? "#111111" : "transparent"
+
+ Behavior on color { ColorAnimation { duration: 150 } }
+
+ Text {
+ id: tabLabel
+ anchors.centerIn: parent
+ text: modelData
+ color: active ? "#ffffff" : "#666666"
+ font.pixelSize: 14
+ font.weight: active ? Font.Medium : Font.Normal
+
+ Behavior on color { ColorAnimation { duration: 150 } }
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ cursorShape: Qt.PointingHandCursor
+ onClicked: {
+ root.currentIndex = index
+ root.tabChanged(index)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}