Compare commits

..

1 Commits

Author SHA1 Message Date
ivan tkachenko
5381c97999 Timeline: Stretch date section sections across the whole ListView width
With paddings, it looked sort of glitched, especially in RTL layouts,
because messages from the top were highlighting all around the padded
section delegate.
2023-09-13 13:05:07 +00:00
9 changed files with 7 additions and 322 deletions

View File

@@ -90,11 +90,6 @@ public:
}),
mentions->end());
}
QStringList suggestions(const QString &word) const
{
return checker->suggest(word);
}
};
ChatDocumentHandler::ChatDocumentHandler(QObject *parent)
@@ -268,47 +263,6 @@ void ChatDocumentHandler::complete(int index)
}
}
QStringList ChatDocumentHandler::getSuggestions(int mousePosition)
{
QTextCursor cursorAtMouse(document()->textDocument());
cursorAtMouse.setPosition(mousePosition);
// Get the word under the (mouse-)cursor and see if it is misspelled.
// Don't include apostrophes at the start/end of the word in the selection.
QTextCursor wordSelectCursor(cursorAtMouse);
wordSelectCursor.clearSelection();
wordSelectCursor.select(QTextCursor::WordUnderCursor);
m_selectedWord = wordSelectCursor.selectedText();
return m_highlighter->suggestions(m_selectedWord);
}
bool ChatDocumentHandler::getActive() const
{
return m_highlighter->settings.checkerEnabledByDefault();
}
bool ChatDocumentHandler::getIsWordIsMisspelled() const
{
return !m_highlighter->errors.isEmpty();
}
QString ChatDocumentHandler::getWordUnderMouse() const
{
return m_selectedWord;
}
void ChatDocumentHandler::replaceWord(const QString &word)
{
QTextCursor cursor(document()->textDocument());
const auto &text = m_room->chatBoxText();
auto at = text.indexOf(m_highlighter->previousText);
cursor.setPosition(at);
cursor.setPosition(at + m_highlighter->previousText.length(), QTextCursor::KeepAnchor);
cursor.insertText(word);
}
CompletionModel *ChatDocumentHandler::completionModel() const
{
return m_completionModel;

View File

@@ -87,10 +87,6 @@ class ChatDocumentHandler : public QObject
*/
Q_PROPERTY(int selectionEnd READ selectionEnd WRITE setSelectionEnd NOTIFY selectionEndChanged)
Q_PROPERTY(bool active READ getActive NOTIFY cursorPositionChanged)
Q_PROPERTY(bool wordIsMisspelled READ getIsWordIsMisspelled NOTIFY cursorPositionChanged)
Q_PROPERTY(QString wordUnderMouse READ getWordUnderMouse NOTIFY cursorPositionChanged)
/**
* @brief The current CompletionModel.
*
@@ -137,12 +133,6 @@ public:
Q_INVOKABLE void complete(int index);
Q_INVOKABLE void replaceWord(const QString &word);
Q_INVOKABLE QStringList getSuggestions(int mousePosition);
bool getActive() const;
bool getIsWordIsMisspelled() const;
QString getWordUnderMouse() const;
void updateCompletions();
CompletionModel *completionModel() const;
@@ -188,6 +178,4 @@ private:
CompletionModel::AutoCompletionType m_completionType = CompletionModel::None;
CompletionModel *m_completionModel = nullptr;
QString m_selectedWord;
};

View File

@@ -285,7 +285,6 @@ int main(int argc, char *argv[])
qmlRegisterUncreatableType<NeoChatRoom>("org.kde.neochat", 1, 0, "NeoChatRoom", {});
qmlRegisterUncreatableType<NeoChatConnection>("org.kde.neochat", 1, 0, "NeoChatConnection", {});
qmlRegisterSingletonType(QUrl("qrc:/ContextMenu.qml"), "org.kde.neochat", 1, 0, "ContextMenu");
qRegisterMetaType<User *>("User*");
qRegisterMetaType<User *>("const User*");
qRegisterMetaType<User *>("const Quotient::User*");

View File

@@ -177,18 +177,6 @@ QQC2.Control {
interval: 5000
}
TapHandler {
enabled: true
acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus
acceptedButtons: Qt.LeftButton | Qt.RightButton
// unfortunately, taphandler's pressed event only triggers when the press is lifted
// we need to use the longpress signal since it triggers when the button is first pressed
longPressThreshold: 0
onLongPressed: ContextMenu.targetClick(point, textField, documentHandler, textField.positionAt(point.position.x, point.position.y));
}
onTextChanged: {
if (!repeatTimer.running && Config.typingNotifications) {
var textExists = text.length > 0

View File

@@ -1,245 +0,0 @@
pragma Singleton
import QtQuick 2.6
import QtQml 2.2
import QtQuick.Controls 2.15
import org.kde.kirigami 2.5 as Kirigami
Menu {
id: contextMenu
property Item target
property bool deselectWhenMenuClosed: true
property int restoredCursorPosition: 0
property int restoredSelectionStart
property int restoredSelectionEnd
property bool persistentSelectionSetting
property var chatdocumenthandler: null
property var suggestions: []
Component.onCompleted: persistentSelectionSetting = persistentSelectionSetting // break binding
property var runOnMenuClose: () => {}
parent: Overlay.overlay
function storeCursorAndSelection() {
contextMenu.restoredCursorPosition = target.cursorPosition;
contextMenu.restoredSelectionStart = target.selectionStart;
contextMenu.restoredSelectionEnd = target.selectionEnd;
}
// target is pressed with mouse
function targetClick(handlerPoint, newTarget, chatdocumenthandler, mousePosition) {
if (handlerPoint.pressedButtons === Qt.RightButton) { // only accept just right click
if (contextMenu.visible) {
deselectWhenMenuClosed = false; // don't deselect text if menu closed by right click on textfield
dismiss();
} else {
contextMenu.target = newTarget;
contextMenu.target.persistentSelection = true; // persist selection when menu is opened
contextMenu.chatdocumenthandler = chatdocumenthandler;
contextMenu.suggestions = mousePosition ? chatdocumenthandler.getSuggestions(mousePosition) : [];
storeCursorAndSelection();
popup(contextMenu.target);
// slightly locate context menu away from mouse so no item is selected when menu is opened
x += 1
y += 1
}
} else {
dismiss();
}
}
// context menu keyboard key
function targetKeyPressed(event, newTarget) {
if (event.modifiers === Qt.NoModifier && event.key === Qt.Key_Menu) {
contextMenu.target = newTarget;
target.persistentSelection = true; // persist selection when menu is opened
storeCursorAndSelection();
popup(contextMenu.target);
}
}
readonly property bool targetIsPassword: target !== null && (target.echoMode === TextInput.PasswordEchoOnEdit || target.echoMode === TextInput.Password)
onAboutToShow: {
if (Overlay.overlay) {
let tempZ = 0
for (let i in Overlay.overlay.visibleChildren) {
tempZ = Math.max(tempZ, Overlay.overlay.visibleChildren[i].z)
}
z = tempZ + 1
}
}
// deal with whether or not text should be deselected
onClosed: {
// restore text field's original persistent selection setting
target.persistentSelection = persistentSelectionSetting
// deselect text field text if menu is closed not because of a right click on the text field
if (deselectWhenMenuClosed) {
target.deselect();
}
deselectWhenMenuClosed = true;
// restore cursor position
target.forceActiveFocus();
target.cursorPosition = restoredCursorPosition;
target.select(restoredSelectionStart, restoredSelectionEnd);
// run action, and free memory
runOnMenuClose();
runOnMenuClose = () => {};
}
onOpened: {
runOnMenuClose = () => {};
}
Instantiator {
active: target !== null && !target.readOnly && chatdocumenthandler !== null && chatdocumenthandler.active && chatdocumenthandler.wordIsMisspelled
model: suggestions
delegate: MenuItem {
text: modelData
onClicked: {
deselectWhenMenuClosed = false;
runOnMenuClose = () => chatdocumenthandler.replaceWord(modelData);
}
}
onObjectAdded: {
contextMenu.insertItem(0, object)
}
onObjectRemoved: contextMenu.removeItem(0)
}
MenuItem {
visible: target !== null && !target.readOnly && chatdocumenthandler !== null && chatdocumenthandler.active && chatdocumenthandler.wordIsMisspelled && suggestions.length === 0
action: Action {
text: chatdocumenthandler ? qsTr("No suggestions for \"%1\"").arg(chatdocumenthandler.wordUnderMouse) : ''
enabled: false
}
}
MenuItem {
visible: target !== null && !target.readOnly && chatdocumenthandler !== null && chatdocumenthandler.active && chatdocumenthandler.wordIsMisspelled
action: Action {
text: chatdocumenthandler ? qsTr("Add \"%1\" to dictionary").arg(chatdocumenthandler.wordUnderMouse) : ''
onTriggered: {
deselectWhenMenuClosed = false;
runOnMenuClose = () => chatdocumenthandler.addWordToDictionary(chatdocumenthandler.wordUnderMouse);
}
}
}
MenuItem {
visible: target !== null && !target.readOnly && chatdocumenthandler !== null && chatdocumenthandler.active && chatdocumenthandler.wordIsMisspelled
action: Action {
text: qsTr("Ignore")
onTriggered: {
deselectWhenMenuClosed = false;
runOnMenuClose = () => chatdocumenthandler.ignoreWord(chatdocumenthandler.wordUnderMouse);
}
}
}
MenuSeparator {
visible: target !== null && !target.readOnly && ((chatdocumenthandler !== null && chatdocumenthandler.active && chatdocumenthandler.wordIsMisspelled))
}
MenuItem {
visible: target !== null && !target.readOnly && !targetIsPassword
action: Action {
icon.name: "edit-undo-symbolic"
text: qsTr("Undo")
shortcut: StandardKey.Undo
}
enabled: target !== null && target.canUndo
onTriggered: {
deselectWhenMenuClosed = false;
runOnMenuClose = () => target.undo();
}
}
MenuItem {
visible: target !== null && !target.readOnly && !targetIsPassword
action: Action {
icon.name: "edit-redo-symbolic"
text: qsTr("Redo")
shortcut: StandardKey.Redo
}
enabled: target !== null && target.canRedo
onTriggered: {
deselectWhenMenuClosed = false;
runOnMenuClose = () => target.redo();
}
}
MenuSeparator {
visible: target !== null && !target.readOnly && !targetIsPassword
}
MenuItem {
visible: target !== null && !target.readOnly && !targetIsPassword
action: Action {
icon.name: "edit-cut-symbolic"
text: qsTr("Cut")
shortcut: StandardKey.Cut
}
enabled: target !== null && target.selectedText
onTriggered: {
deselectWhenMenuClosed = false;
runOnMenuClose = () => target.cut();
}
}
MenuItem {
action: Action {
icon.name: "edit-copy-symbolic"
text: qsTr("Copy")
shortcut: StandardKey.Copy
}
enabled: target !== null && target.selectedText
visible: !targetIsPassword
onTriggered: {
deselectWhenMenuClosed = false;
runOnMenuClose = () => target.copy();
}
}
MenuItem {
visible: target !== null && !target.readOnly
action: Action {
icon.name: "edit-paste-symbolic"
text: qsTr("Paste")
shortcut: StandardKey.Paste
}
enabled: target !== null && target.canPaste
onTriggered: {
deselectWhenMenuClosed = false;
runOnMenuClose = () => target.paste();
}
}
MenuItem {
visible: target !== null && !target.readOnly
action: Action {
icon.name: "edit-delete-symbolic"
text: qsTr("Delete")
shortcut: StandardKey.Delete
}
enabled: target !== null && target.selectedText
onTriggered: {
deselectWhenMenuClosed = false;
runOnMenuClose = () => target.remove(target.selectionStart, target.selectionEnd);
}
}
MenuSeparator {
visible: !targetIsPassword
}
MenuItem {
action: Action {
icon.name: "edit-select-all-symbolic"
text: qsTr("Select All")
shortcut: StandardKey.SelectAll
}
visible: !targetIsPassword
onTriggered: {
deselectWhenMenuClosed = false;
runOnMenuClose = () => target.selectAll();
}
}
}

View File

@@ -19,6 +19,8 @@ QQC2.ItemDelegate {
property int colorSet: Kirigami.Theme.Window
topPadding: Kirigami.Units.largeSpacing
leftPadding: 0
rightPadding: 0
bottomPadding: 0 // Note not 0 by default
contentItem: ColumnLayout {

View File

@@ -342,6 +342,8 @@ ColumnLayout {
SectionDelegate {
id: sectionDelegate
Layout.fillWidth: true
// Fill ListView width without affecting size helper and the rest of components too much
Layout.rightMargin: root.parent ? root.width - root.parent.width : 0
visible: root.showSection
labelText: root.section
colorSet: Config.compactLayout || root.alwaysMaxWidth ? Kirigami.Theme.View : Kirigami.Theme.Window

View File

@@ -36,7 +36,7 @@ QQC2.ScrollView {
readonly property NeoChatRoom currentRoom: root.currentRoom
readonly property int largestVisibleIndex: count > 0 ? indexAt(contentX + (width / 2), contentY + height - 1) : -1
readonly property var sectionBannerItem: contentHeight >= height ? itemAtIndex(sectionBannerIndex()) : undefined
readonly property Item sectionBannerItem: contentHeight >= height ? itemAtIndex(sectionBannerIndex()) : null
// Spacing needs to be zero or the top sectionLabel overlay will be disrupted.
// This is because itemAt returns null in the spaces.
@@ -108,13 +108,11 @@ QQC2.ScrollView {
footer: SectionDelegate {
id: sectionBanner
anchors.left: parent.left
anchors.leftMargin: messageListView.sectionBannerItem ? messageListView.sectionBannerItem.x : 0
anchors.right: parent.right
width: ListView.view ? ListView.view.width : 0
maxWidth: Config.compactLayout ? messageListView.width : (messageListView.sectionBannerItem ? messageListView.sectionBannerItem.width - Kirigami.Units.largeSpacing * 2 : 0)
z: 3
visible: !!messageListView.sectionBannerItem && messageListView.sectionBannerItem.ListView.section !== "" && !Config.blur
visible: messageListView.sectionBannerItem && messageListView.sectionBannerItem.ListView.section !== "" && !Config.blur
labelText: messageListView.sectionBannerItem ? messageListView.sectionBannerItem.ListView.section : ""
colorSet: Config.compactLayout ? Kirigami.Theme.View : Kirigami.Theme.Window
}

View File

@@ -36,7 +36,6 @@
<file alias="HoverActions.qml">qml/Component/HoverActions.qml</file>
<file alias="ChatBox.qml">qml/Component/ChatBox/ChatBox.qml</file>
<file alias="ChatBar.qml">qml/Component/ChatBox/ChatBar.qml</file>
<file alias="ContextMenu.qml">qml/Component/ChatBox/ContextMenu.qml</file>
<file alias="AttachmentPane.qml">qml/Component/ChatBox/AttachmentPane.qml</file>
<file alias="ReplyPane.qml">qml/Component/ChatBox/ReplyPane.qml</file>
<file alias="CompletionMenu.qml">qml/Component/ChatBox/CompletionMenu.qml</file>