Chatview and combobox UI fixes (#2489)

Chatview and combobox UI fixes (#2489)

Signed-off-by: Adam Treat <treat.adam@gmail.com>
This commit is contained in:
AT 2024-06-29 18:00:52 -04:00 committed by GitHub
parent 55858f93b0
commit b5fdc4c05a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 236 additions and 203 deletions

View File

@ -208,6 +208,7 @@ qt_add_qml_module(chat
icons/thumbs_up.svg icons/thumbs_up.svg
icons/thumbs_down.svg icons/thumbs_down.svg
icons/twitter.svg icons/twitter.svg
icons/up_down.svg
icons/left_panel_closed.svg icons/left_panel_closed.svg
icons/left_panel_open.svg icons/left_panel_open.svg
icons/gpt4all.svg icons/gpt4all.svg

View File

@ -1,6 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M232,200a8,8,0,0,1-8,8H32a8,8,0,1,1,0-16H224A8,8,0,0,1,232,200ZM25.59,150.84a16,16,0,0,1,2-17.07L109.26,32.94a24.11,24.11,0,0,1,37.48,0l81.65,100.83A16.1,16.1,0,0,1,215.91,160H40.09A16,16,0,0,1,25.59,150.84ZM40,143.91s0,.09.08.11l175.83,0s.08-.09.08-.13L134.3,43a8.1,8.1,0,0,0-12.6,0L40,143.84A.28.28,0,0,0,40,143.91Z"></path></svg>
<svg xmlns="http://www.w3.org/2000/svg" fill="#7d7d8e" viewBox="0 0 448 512"><path d="M448 384v64c0 17.673-14.327 32-32 32H32c-17.673 0-32-14.327-32-32v-64c0-17.673 14.327-32 32-32h384c17.673 0 32 14.327 32 32zM48.053 320h351.886c41.651 0 63.581-49.674 35.383-80.435L259.383 47.558c-19.014-20.743-51.751-20.744-70.767 0L12.67 239.565C-15.475 270.268 6.324 320 48.053 320z"/></svg>
<!--
Font Awesome Free 5.2.0 by @fontawesome - https://fontawesome.com
License - https://fontawesome.com/license (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
-->

Before

Width:  |  Height:  |  Size: 557 B

After

Width:  |  Height:  |  Size: 441 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M181.66,170.34a8,8,0,0,1,0,11.32l-48,48a8,8,0,0,1-11.32,0l-48-48a8,8,0,0,1,11.32-11.32L128,212.69l42.34-42.35A8,8,0,0,1,181.66,170.34Zm-96-84.68L128,43.31l42.34,42.35a8,8,0,0,0,11.32-11.32l-48-48a8,8,0,0,0-11.32,0l-48,48A8,8,0,0,0,85.66,85.66Z"></path></svg>

After

Width:  |  Height:  |  Size: 367 B

View File

@ -205,13 +205,12 @@ Rectangle {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
spacing: 20 spacing: 0
Rectangle { Rectangle {
Layout.alignment: Qt.AlignLeft Layout.alignment: Qt.AlignLeft
Layout.leftMargin: 30 Layout.leftMargin: 30
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredWidth: 100
color: "transparent" color: "transparent"
Layout.preferredHeight: childrenRect.height Layout.preferredHeight: childrenRect.height
MyToolButton { MyToolButton {
@ -237,8 +236,15 @@ Rectangle {
id: comboBox id: comboBox
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
Layout.fillHeight: true Layout.fillHeight: true
Layout.preferredWidth: 350 Layout.preferredWidth: 550
Layout.maximumWidth: 675 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 enabled: !currentChat.isServer
&& !currentChat.trySwitchContextInProgress && !currentChat.trySwitchContextInProgress
&& !currentChat.isCurrentlyLoading && !currentChat.isCurrentlyLoading
@ -266,8 +272,8 @@ Rectangle {
ProgressBar { ProgressBar {
id: modelProgress id: modelProgress
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.left: parent.left anchors.horizontalCenter: parent.horizontalCenter
anchors.right: parent.right width: contentRow.width + 20
visible: currentChat.isCurrentlyLoading visible: currentChat.isCurrentlyLoading
height: 10 height: 10
value: currentChat.modelLoadingPercentage value: currentChat.modelLoadingPercentage
@ -286,38 +292,110 @@ Rectangle {
} }
} }
} }
contentItem: Text {
anchors.horizontalCenter: parent.horizontalCenter contentItem: Item {
leftPadding: 10 RowLayout {
rightPadding: { id: contentRow
if (ejectButton.visible && reloadButton) anchors.centerIn: parent
return 105; spacing: 0
if (reloadButton.visible) RowLayout {
return 65 id: miniButtonsRow
return 25 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 { delegate: ItemDelegate {
id: comboItemDelegate id: comboItemDelegate
@ -330,12 +408,12 @@ Rectangle {
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
} }
background: Rectangle { background: Rectangle {
color: (index % 2 === 0 ? theme.darkContrast : theme.lightContrast) color: highlighted ? theme.lightContrast : theme.darkContrast
border.width: highlighted
border.color: theme.accentColor
} }
highlighted: comboBox.highlightedIndex === index highlighted: comboBox.highlightedIndex === index
} }
indicator: Item {
}
popup: Popup { popup: Popup {
id: comboItemPopup id: comboItemPopup
y: comboBox.height - 1 y: comboBox.height - 1
@ -378,46 +456,6 @@ Rectangle {
comboBox.changeModel(index); 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 { Rectangle {
@ -425,99 +463,97 @@ Rectangle {
Layout.alignment: Qt.AlignRight Layout.alignment: Qt.AlignRight
Layout.rightMargin: 30 Layout.rightMargin: 30
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredWidth: 100
Layout.preferredHeight: childrenRect.height Layout.preferredHeight: childrenRect.height
clip: true
RowLayout { MyButton {
spacing: 20 id: collectionsButton
clip: true
anchors.right: parent.right anchors.right: parent.right
MyButton { borderWidth: 0
id: collectionsButton backgroundColor: theme.collectionsButtonBackground
borderWidth: 0 backgroundColorHovered: theme.collectionsButtonBackgroundHovered
backgroundColor: theme.collectionsButtonBackground backgroundRadius: 5
backgroundColorHovered: theme.collectionsButtonBackgroundHovered padding: 15
backgroundRadius: 5 topPadding: 8
padding: 15 bottomPadding: 8
topPadding: 8
bottomPadding: 8
contentItem: RowLayout { contentItem: RowLayout {
spacing: 10 spacing: 10
Item { Item {
visible: currentChat.collectionModel.count === 0 visible: currentChat.collectionModel.count === 0
Layout.minimumWidth: collectionsImage.width Layout.minimumWidth: collectionsImage.width
Layout.minimumHeight: collectionsImage.height Layout.minimumHeight: collectionsImage.height
Image { Image {
id: collectionsImage id: collectionsImage
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
sourceSize.width: 24 sourceSize.width: 24
sourceSize.height: 24 sourceSize.height: 24
mipmap: true mipmap: true
visible: false visible: false
source: "qrc:/gpt4all/icons/db.svg" source: "qrc:/gpt4all/icons/db.svg"
}
ColorOverlay {
anchors.fill: collectionsImage
source: collectionsImage
color: theme.collectionsButtonForeground
}
} }
MyBusyIndicator { ColorOverlay {
visible: currentChat.collectionModel.updatingCount !== 0 anchors.fill: collectionsImage
color: theme.collectionsButtonProgress source: collectionsImage
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
color: theme.collectionsButtonForeground 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 {
text: qsTr("LocalDocs") anchors.centerIn: parent
text: currentChat.collectionModel.updatingCount
color: theme.collectionsButtonForeground color: theme.collectionsButtonForeground
font.pixelSize: theme.fontSizeLarge font.pixelSize: 14 // fixed regardless of theme
} }
} }
fontPixelSize: theme.fontSizeLarge Rectangle {
visible: currentChat.collectionModel.count !== 0
background: Rectangle { radius: 6
radius: collectionsButton.backgroundRadius color: theme.collectionsButtonForeground
// TODO(jared): either use collectionsButton-specific theming, or don't - this is inconsistent Layout.minimumWidth: collectionsImage.width
color: conversation.state == "expanded" ? ( Layout.minimumHeight: collectionsImage.height
collectionsButton.hovered ? theme.lightButtonBackgroundHovered : theme.lightButtonBackground Text {
) : ( anchors.centerIn: parent
collectionsButton.hovered ? theme.lighterButtonBackground : theme.lighterButtonBackgroundHovered text: currentChat.collectionModel.count
) color: theme.collectionsButtonText
font.pixelSize: 14 // fixed regardless of theme
}
} }
Accessible.name: qsTr("Add documents") Text {
Accessible.description: qsTr("add collections of documents to the chat") text: qsTr("LocalDocs")
color: theme.collectionsButtonForeground
onClicked: { font.pixelSize: theme.fontSizeLarge
conversation.toggleRightPanel()
} }
} }
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 { MyTextArea {
id: textInput id: textInput
color: theme.textColor color: theme.textColor
topPadding: 30 topPadding: 15
bottomPadding: 30 bottomPadding: 15
leftPadding: 20 leftPadding: 20
rightPadding: 40 rightPadding: 40
enabled: currentChat.isModelLoaded && !currentChat.isServer enabled: currentChat.isModelLoaded && !currentChat.isServer
@ -1506,8 +1542,8 @@ Rectangle {
anchors.right: textInputView.right anchors.right: textInputView.right
anchors.verticalCenter: textInputView.verticalCenter anchors.verticalCenter: textInputView.verticalCenter
anchors.rightMargin: 15 anchors.rightMargin: 15
width: 30 imageWidth: theme.fontSizeLarger
height: 30 imageHeight: theme.fontSizeLarger
visible: !currentChat.isServer && ModelList.selectableModels.count !== 0 visible: !currentChat.isServer && ModelList.selectableModels.count !== 0
enabled: !currentChat.responseInProgress enabled: !currentChat.responseInProgress
source: "qrc:/gpt4all/icons/send_message.svg" source: "qrc:/gpt4all/icons/send_message.svg"

View File

@ -1,6 +1,8 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Controls.Basic import QtQuick.Controls.Basic
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
ComboBox { ComboBox {
id: comboBox id: comboBox
@ -8,15 +10,39 @@ ComboBox {
spacing: 0 spacing: 0
padding: 10 padding: 10
Accessible.role: Accessible.ComboBox Accessible.role: Accessible.ComboBox
contentItem: Text { contentItem: RowLayout {
id: text id: contentRow
leftPadding: 10 spacing: 0
rightPadding: 20 Text {
text: comboBox.displayText id: text
font: comboBox.font Layout.fillWidth: true
color: theme.textColor leftPadding: 10
verticalAlignment: Text.AlignVCenter rightPadding: 20
elide: Text.ElideRight 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 { delegate: ItemDelegate {
width: comboBox.width width: comboBox.width
@ -53,33 +79,7 @@ ComboBox {
color: theme.black color: theme.black
} }
} }
indicator: Canvas { indicator: Item {
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();
}
} }
background: Rectangle { background: Rectangle {
color: theme.controlBackground color: theme.controlBackground

View File

@ -33,8 +33,8 @@ Button {
anchors.centerIn: parent anchors.centerIn: parent
visible: false visible: false
mipmap: true mipmap: true
sourceSize.width: 20 sourceSize.width: 16
sourceSize.height: 20 sourceSize.height: 16
} }
ColorOverlay { ColorOverlay {
anchors.fill: image anchors.fill: image