diff --git a/gpt4all-chat/CMakeLists.txt b/gpt4all-chat/CMakeLists.txt
index 034a5101..8bd4d483 100644
--- a/gpt4all-chat/CMakeLists.txt
+++ b/gpt4all-chat/CMakeLists.txt
@@ -208,6 +208,7 @@ qt_add_qml_module(chat
icons/thumbs_up.svg
icons/thumbs_down.svg
icons/twitter.svg
+ icons/up_down.svg
icons/left_panel_closed.svg
icons/left_panel_open.svg
icons/gpt4all.svg
diff --git a/gpt4all-chat/icons/eject.svg b/gpt4all-chat/icons/eject.svg
index 9649c487..1c0e004e 100644
--- a/gpt4all-chat/icons/eject.svg
+++ b/gpt4all-chat/icons/eject.svg
@@ -1,6 +1 @@
-
-
-
+
\ No newline at end of file
diff --git a/gpt4all-chat/icons/up_down.svg b/gpt4all-chat/icons/up_down.svg
new file mode 100644
index 00000000..4e11ebad
--- /dev/null
+++ b/gpt4all-chat/icons/up_down.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/gpt4all-chat/qml/ChatView.qml b/gpt4all-chat/qml/ChatView.qml
index 5b12c838..dca00017 100644
--- a/gpt4all-chat/qml/ChatView.qml
+++ b/gpt4all-chat/qml/ChatView.qml
@@ -205,13 +205,12 @@ Rectangle {
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
- spacing: 20
+ spacing: 0
Rectangle {
Layout.alignment: Qt.AlignLeft
Layout.leftMargin: 30
Layout.fillWidth: true
- Layout.preferredWidth: 100
color: "transparent"
Layout.preferredHeight: childrenRect.height
MyToolButton {
@@ -237,8 +236,15 @@ Rectangle {
id: comboBox
Layout.alignment: Qt.AlignHCenter
Layout.fillHeight: true
- Layout.preferredWidth: 350
- Layout.maximumWidth: 675
+ Layout.preferredWidth: 550
+ Layout.leftMargin: {
+ // This function works in tandem with the preferredWidth and the layout to
+ // provide the maximum size combobox we can have at the smallest window width
+ // we allow with the largest font size we allow. It is unfortunately based
+ // upon a magic number that was produced through trial and error for something
+ // I don't fully understand.
+ return -Math.max(0, comboBox.width / 2 + collectionsButton.width + 110 /*magic*/ - comboLayout.width / 2);
+ }
enabled: !currentChat.isServer
&& !currentChat.trySwitchContextInProgress
&& !currentChat.isCurrentlyLoading
@@ -266,8 +272,8 @@ Rectangle {
ProgressBar {
id: modelProgress
anchors.bottom: parent.bottom
- anchors.left: parent.left
- anchors.right: parent.right
+ anchors.horizontalCenter: parent.horizontalCenter
+ width: contentRow.width + 20
visible: currentChat.isCurrentlyLoading
height: 10
value: currentChat.modelLoadingPercentage
@@ -286,38 +292,110 @@ Rectangle {
}
}
}
- contentItem: Text {
- anchors.horizontalCenter: parent.horizontalCenter
- leftPadding: 10
- rightPadding: {
- if (ejectButton.visible && reloadButton)
- return 105;
- if (reloadButton.visible)
- return 65
- return 25
+
+ contentItem: Item {
+ RowLayout {
+ id: contentRow
+ anchors.centerIn: parent
+ spacing: 0
+ RowLayout {
+ id: miniButtonsRow
+ clip: true
+ Behavior on Layout.preferredWidth {
+ NumberAnimation {
+ duration: 300
+ easing.type: Easing.InOutQuad
+ }
+ }
+
+ Layout.preferredWidth: {
+ if (!comboBox.hovered)
+ return 0
+ return (reloadButton.visible ? reloadButton.width : 0) + (ejectButton.visible ? ejectButton.width : 0)
+ }
+
+ MyMiniButton {
+ id: reloadButton
+ Layout.alignment: Qt.AlignCenter
+ visible: currentChat.modelLoadingError === ""
+ && !currentChat.trySwitchContextInProgress
+ && !currentChat.isCurrentlyLoading
+ && (currentChat.isModelLoaded || currentModelName() !== "")
+ source: "qrc:/gpt4all/icons/regenerate.svg"
+ backgroundColor: theme.textColor
+ backgroundColorHovered: theme.styledTextColor
+ onClicked: {
+ if (currentChat.isModelLoaded)
+ currentChat.forceReloadModel();
+ else
+ currentChat.reloadModel();
+ }
+ ToolTip.text: qsTr("Reload the currently loaded model")
+ ToolTip.visible: hovered
+ }
+
+ MyMiniButton {
+ id: ejectButton
+ Layout.alignment: Qt.AlignCenter
+ visible: currentChat.isModelLoaded && !currentChat.isCurrentlyLoading
+ source: "qrc:/gpt4all/icons/eject.svg"
+ backgroundColor: theme.textColor
+ backgroundColorHovered: theme.styledTextColor
+ onClicked: {
+ currentChat.forceUnloadModel();
+ }
+ ToolTip.text: qsTr("Eject the currently loaded model")
+ ToolTip.visible: hovered
+ }
+ }
+
+ Text {
+ id: comboBoxText
+ leftPadding: 10
+ rightPadding: 10
+ text: {
+ if (ModelList.selectableModels.count === 0)
+ return qsTr("No model installed...")
+ if (currentChat.modelLoadingError !== "")
+ return qsTr("Model loading error...")
+ if (currentChat.trySwitchContextInProgress === 1)
+ return qsTr("Waiting for model...")
+ if (currentChat.trySwitchContextInProgress === 2)
+ return qsTr("Switching context...")
+ if (currentModelName() === "")
+ return qsTr("Choose a model...")
+ if (currentChat.modelLoadingPercentage === 0.0)
+ return qsTr("Reload \u00B7 ") + currentModelName()
+ if (currentChat.isCurrentlyLoading)
+ return qsTr("Loading \u00B7 ") + currentModelName()
+ return currentModelName()
+ }
+ font.pixelSize: theme.fontSizeLarger
+ color: theme.iconBackgroundLight
+ verticalAlignment: Text.AlignVCenter
+ horizontalAlignment: Text.AlignHCenter
+ elide: Text.ElideRight
+ }
+ Item {
+ Layout.minimumWidth: updown.width
+ Layout.minimumHeight: updown.height
+ Image {
+ id: updown
+ anchors.verticalCenter: parent.verticalCenter
+ sourceSize.width: comboBoxText.font.pixelSize
+ sourceSize.height: comboBoxText.font.pixelSize
+ mipmap: true
+ visible: false
+ source: "qrc:/gpt4all/icons/up_down.svg"
+ }
+
+ ColorOverlay {
+ anchors.fill: updown
+ source: updown
+ color: comboBoxText.color
+ }
+ }
}
- text: {
- if (ModelList.selectableModels.count === 0)
- return qsTr("No model installed...")
- if (currentChat.modelLoadingError !== "")
- return qsTr("Model loading error...")
- if (currentChat.trySwitchContextInProgress === 1)
- return qsTr("Waiting for model...")
- if (currentChat.trySwitchContextInProgress === 2)
- return qsTr("Switching context...")
- if (currentModelName() === "")
- return qsTr("Choose a model...")
- if (currentChat.modelLoadingPercentage === 0.0)
- return qsTr("Reload \u00B7 ") + currentModelName()
- if (currentChat.isCurrentlyLoading)
- return qsTr("Loading \u00B7 ") + currentModelName()
- return currentModelName()
- }
- font.pixelSize: theme.fontSizeLarger
- color: theme.iconBackgroundLight
- verticalAlignment: Text.AlignVCenter
- horizontalAlignment: Text.AlignHCenter
- elide: Text.ElideRight
}
delegate: ItemDelegate {
id: comboItemDelegate
@@ -330,12 +408,12 @@ Rectangle {
verticalAlignment: Text.AlignVCenter
}
background: Rectangle {
- color: (index % 2 === 0 ? theme.darkContrast : theme.lightContrast)
- border.width: highlighted
- border.color: theme.accentColor
+ color: highlighted ? theme.lightContrast : theme.darkContrast
}
highlighted: comboBox.highlightedIndex === index
}
+ indicator: Item {
+ }
popup: Popup {
id: comboItemPopup
y: comboBox.height - 1
@@ -378,46 +456,6 @@ Rectangle {
comboBox.changeModel(index);
}
}
-
- MyMiniButton {
- id: ejectButton
- visible: currentChat.isModelLoaded && !currentChat.isCurrentlyLoading
- z: 500
- anchors.right: parent.right
- anchors.rightMargin: 50
- anchors.verticalCenter: parent.verticalCenter
- source: "qrc:/gpt4all/icons/eject.svg"
- backgroundColor: theme.mutedLightTextColor
- backgroundColorHovered: theme.iconBackgroundLight
- onClicked: {
- currentChat.forceUnloadModel();
- }
- ToolTip.text: qsTr("Eject the currently loaded model")
- ToolTip.visible: hovered
- }
-
- MyMiniButton {
- id: reloadButton
- visible: currentChat.modelLoadingError === ""
- && !currentChat.trySwitchContextInProgress
- && !currentChat.isCurrentlyLoading
- && (currentChat.isModelLoaded || currentModelName() !== "")
- z: 500
- anchors.right: ejectButton.visible ? ejectButton.left : parent.right
- anchors.rightMargin: ejectButton.visible ? 10 : 50
- anchors.verticalCenter: parent.verticalCenter
- source: "qrc:/gpt4all/icons/regenerate.svg"
- backgroundColor: theme.mutedLightTextColor
- backgroundColorHovered: theme.iconBackgroundLight
- onClicked: {
- if (currentChat.isModelLoaded)
- currentChat.forceReloadModel();
- else
- currentChat.reloadModel();
- }
- ToolTip.text: qsTr("Reload the currently loaded model")
- ToolTip.visible: hovered
- }
}
Rectangle {
@@ -425,99 +463,97 @@ Rectangle {
Layout.alignment: Qt.AlignRight
Layout.rightMargin: 30
Layout.fillWidth: true
- Layout.preferredWidth: 100
Layout.preferredHeight: childrenRect.height
+ clip: true
- RowLayout {
- spacing: 20
+ MyButton {
+ id: collectionsButton
+ clip: true
anchors.right: parent.right
- MyButton {
- id: collectionsButton
- borderWidth: 0
- backgroundColor: theme.collectionsButtonBackground
- backgroundColorHovered: theme.collectionsButtonBackgroundHovered
- backgroundRadius: 5
- padding: 15
- topPadding: 8
- bottomPadding: 8
+ borderWidth: 0
+ backgroundColor: theme.collectionsButtonBackground
+ backgroundColorHovered: theme.collectionsButtonBackgroundHovered
+ backgroundRadius: 5
+ padding: 15
+ topPadding: 8
+ bottomPadding: 8
- contentItem: RowLayout {
- spacing: 10
- Item {
- visible: currentChat.collectionModel.count === 0
- Layout.minimumWidth: collectionsImage.width
- Layout.minimumHeight: collectionsImage.height
- Image {
- id: collectionsImage
- anchors.verticalCenter: parent.verticalCenter
- sourceSize.width: 24
- sourceSize.height: 24
- mipmap: true
- visible: false
- source: "qrc:/gpt4all/icons/db.svg"
- }
-
- ColorOverlay {
- anchors.fill: collectionsImage
- source: collectionsImage
- color: theme.collectionsButtonForeground
- }
+ contentItem: RowLayout {
+ spacing: 10
+ Item {
+ visible: currentChat.collectionModel.count === 0
+ Layout.minimumWidth: collectionsImage.width
+ Layout.minimumHeight: collectionsImage.height
+ Image {
+ id: collectionsImage
+ anchors.verticalCenter: parent.verticalCenter
+ sourceSize.width: 24
+ sourceSize.height: 24
+ mipmap: true
+ visible: false
+ source: "qrc:/gpt4all/icons/db.svg"
}
- MyBusyIndicator {
- visible: currentChat.collectionModel.updatingCount !== 0
- color: theme.collectionsButtonProgress
- size: 24
- Layout.minimumWidth: 24
- Layout.minimumHeight: 24
- Text {
- anchors.centerIn: parent
- text: currentChat.collectionModel.updatingCount
- color: theme.collectionsButtonForeground
- font.pixelSize: 14 // fixed regardless of theme
- }
- }
-
- Rectangle {
- visible: currentChat.collectionModel.count !== 0
- radius: 6
+ ColorOverlay {
+ anchors.fill: collectionsImage
+ source: collectionsImage
color: theme.collectionsButtonForeground
- Layout.minimumWidth: collectionsImage.width
- Layout.minimumHeight: collectionsImage.height
- Text {
- anchors.centerIn: parent
- text: currentChat.collectionModel.count
- color: theme.collectionsButtonText
- font.pixelSize: 14 // fixed regardless of theme
- }
}
+ }
+ MyBusyIndicator {
+ visible: currentChat.collectionModel.updatingCount !== 0
+ color: theme.collectionsButtonProgress
+ size: 24
+ Layout.minimumWidth: 24
+ Layout.minimumHeight: 24
Text {
- text: qsTr("LocalDocs")
+ anchors.centerIn: parent
+ text: currentChat.collectionModel.updatingCount
color: theme.collectionsButtonForeground
- font.pixelSize: theme.fontSizeLarge
+ font.pixelSize: 14 // fixed regardless of theme
}
}
- fontPixelSize: theme.fontSizeLarge
-
- background: Rectangle {
- radius: collectionsButton.backgroundRadius
- // TODO(jared): either use collectionsButton-specific theming, or don't - this is inconsistent
- color: conversation.state == "expanded" ? (
- collectionsButton.hovered ? theme.lightButtonBackgroundHovered : theme.lightButtonBackground
- ) : (
- collectionsButton.hovered ? theme.lighterButtonBackground : theme.lighterButtonBackgroundHovered
- )
+ Rectangle {
+ visible: currentChat.collectionModel.count !== 0
+ radius: 6
+ color: theme.collectionsButtonForeground
+ Layout.minimumWidth: collectionsImage.width
+ Layout.minimumHeight: collectionsImage.height
+ Text {
+ anchors.centerIn: parent
+ text: currentChat.collectionModel.count
+ color: theme.collectionsButtonText
+ font.pixelSize: 14 // fixed regardless of theme
+ }
}
- Accessible.name: qsTr("Add documents")
- Accessible.description: qsTr("add collections of documents to the chat")
-
- onClicked: {
- conversation.toggleRightPanel()
+ Text {
+ text: qsTr("LocalDocs")
+ color: theme.collectionsButtonForeground
+ font.pixelSize: theme.fontSizeLarge
}
}
+
+ fontPixelSize: theme.fontSizeLarge
+
+ background: Rectangle {
+ radius: collectionsButton.backgroundRadius
+ // TODO(jared): either use collectionsButton-specific theming, or don't - this is inconsistent
+ color: conversation.state === "expanded" ? (
+ collectionsButton.hovered ? theme.lightButtonBackgroundHovered : theme.lightButtonBackground
+ ) : (
+ collectionsButton.hovered ? theme.lighterButtonBackground : theme.lighterButtonBackgroundHovered
+ )
+ }
+
+ Accessible.name: qsTr("Add documents")
+ Accessible.description: qsTr("add collections of documents to the chat")
+
+ onClicked: {
+ conversation.toggleRightPanel()
+ }
}
}
}
@@ -1418,8 +1454,8 @@ Rectangle {
MyTextArea {
id: textInput
color: theme.textColor
- topPadding: 30
- bottomPadding: 30
+ topPadding: 15
+ bottomPadding: 15
leftPadding: 20
rightPadding: 40
enabled: currentChat.isModelLoaded && !currentChat.isServer
@@ -1506,8 +1542,8 @@ Rectangle {
anchors.right: textInputView.right
anchors.verticalCenter: textInputView.verticalCenter
anchors.rightMargin: 15
- width: 30
- height: 30
+ imageWidth: theme.fontSizeLarger
+ imageHeight: theme.fontSizeLarger
visible: !currentChat.isServer && ModelList.selectableModels.count !== 0
enabled: !currentChat.responseInProgress
source: "qrc:/gpt4all/icons/send_message.svg"
diff --git a/gpt4all-chat/qml/MyComboBox.qml b/gpt4all-chat/qml/MyComboBox.qml
index eca28256..7f25c60c 100644
--- a/gpt4all-chat/qml/MyComboBox.qml
+++ b/gpt4all-chat/qml/MyComboBox.qml
@@ -1,6 +1,8 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
+import QtQuick.Layouts
+import Qt5Compat.GraphicalEffects
ComboBox {
id: comboBox
@@ -8,15 +10,39 @@ ComboBox {
spacing: 0
padding: 10
Accessible.role: Accessible.ComboBox
- contentItem: Text {
- id: text
- leftPadding: 10
- rightPadding: 20
- text: comboBox.displayText
- font: comboBox.font
- color: theme.textColor
- verticalAlignment: Text.AlignVCenter
- elide: Text.ElideRight
+ contentItem: RowLayout {
+ id: contentRow
+ spacing: 0
+ Text {
+ id: text
+ Layout.fillWidth: true
+ leftPadding: 10
+ rightPadding: 20
+ text: comboBox.displayText
+ font: comboBox.font
+ color: theme.textColor
+ verticalAlignment: Text.AlignLeft
+ elide: Text.ElideRight
+ }
+ Item {
+ Layout.preferredWidth: updown.width
+ Layout.preferredHeight: updown.height
+ Image {
+ id: updown
+ anchors.verticalCenter: parent.verticalCenter
+ sourceSize.width: comboBox.font.pixelSize
+ sourceSize.height: comboBox.font.pixelSize
+ mipmap: true
+ visible: false
+ source: "qrc:/gpt4all/icons/up_down.svg"
+ }
+
+ ColorOverlay {
+ anchors.fill: updown
+ source: updown
+ color: theme.textColor
+ }
+ }
}
delegate: ItemDelegate {
width: comboBox.width
@@ -53,33 +79,7 @@ ComboBox {
color: theme.black
}
}
- indicator: Canvas {
- id: canvas
- x: comboBox.width - width - comboBox.rightPadding
- y: comboBox.topPadding + (comboBox.availableHeight - height) / 2
- width: 12
- height: 18
- contextType: "2d"
-
- Connections {
- target: comboBox
- function onPressedChanged() { canvas.requestPaint(); }
- }
-
- onPaint: {
- var context = getContext("2d");
- context.reset();
- context.lineWidth = 2;
- context.moveTo(0, height / 2 - 2);
- context.lineTo(width / 2, 0);
- context.lineTo(width, height / 2 - 2);
- context.moveTo(0, height / 2 + 2);
- context.lineTo(width / 2, height);
- context.lineTo(width, height / 2 + 2);
- context.strokeStyle = comboBox.pressed ? theme.mutedLightTextColor : theme.mutedLighterTextColor;
- context.stroke();
-
- }
+ indicator: Item {
}
background: Rectangle {
color: theme.controlBackground
diff --git a/gpt4all-chat/qml/MyMiniButton.qml b/gpt4all-chat/qml/MyMiniButton.qml
index 293026cd..2d3ef728 100644
--- a/gpt4all-chat/qml/MyMiniButton.qml
+++ b/gpt4all-chat/qml/MyMiniButton.qml
@@ -33,8 +33,8 @@ Button {
anchors.centerIn: parent
visible: false
mipmap: true
- sourceSize.width: 20
- sourceSize.height: 20
+ sourceSize.width: 16
+ sourceSize.height: 16
}
ColorOverlay {
anchors.fill: image