/* * Copyright (C) 2025 Petr Mironychev * * This file is part of QodeAssist. * * QodeAssist is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * QodeAssist is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with QodeAssist. If not, see . */ #include "RefactorSuggestionHoverHandler.hpp" #include "RefactorSuggestion.hpp" #include #include #include #include #include #include #include #include #include #include #include #include namespace QodeAssist { RefactorSuggestionHoverHandler::RefactorSuggestionHoverHandler() { setPriority(Priority_Suggestion); } void RefactorSuggestionHoverHandler::setSuggestionRange(const Utils::Text::Range &range) { m_suggestionRange = range; m_hasSuggestion = true; } void RefactorSuggestionHoverHandler::clearSuggestionRange() { m_hasSuggestion = false; } void RefactorSuggestionHoverHandler::identifyMatch( TextEditor::TextEditorWidget *editorWidget, int pos, ReportPriority report) { LOG_MESSAGE(QString("RefactorSuggestionHoverHandler::identifyMatch pos=%1, hasSuggestion=%2, suggestionVisible=%3") .arg(pos) .arg(m_hasSuggestion) .arg(editorWidget->suggestionVisible())); QScopeGuard cleanup([&] { report(Priority_None); }); if (!editorWidget->suggestionVisible()) { LOG_MESSAGE("RefactorSuggestionHoverHandler: No suggestion visible"); return; } QTextCursor cursor(editorWidget->document()); cursor.setPosition(pos); m_block = cursor.block(); // Check if this block has a suggestion #if QODEASSIST_QT_CREATOR_VERSION_MAJOR >= 17 auto *suggestion = dynamic_cast( TextEditor::TextBlockUserData::suggestion(m_block)); #else auto *userData = TextEditor::TextDocumentLayout::textUserData(m_block); if (!userData) { LOG_MESSAGE("RefactorSuggestionHoverHandler: No user data in block"); return; } auto *suggestion = dynamic_cast(userData->suggestion()); #endif if (!suggestion) { LOG_MESSAGE("RefactorSuggestionHoverHandler: No RefactorSuggestion in block"); return; } LOG_MESSAGE("RefactorSuggestionHoverHandler: Found RefactorSuggestion, reporting Priority_Suggestion"); cleanup.dismiss(); report(Priority_Suggestion); } void RefactorSuggestionHoverHandler::operateTooltip( TextEditor::TextEditorWidget *editorWidget, const QPoint &point) { Q_UNUSED(point) #if QODEASSIST_QT_CREATOR_VERSION_MAJOR >= 17 auto *suggestion = dynamic_cast( TextEditor::TextBlockUserData::suggestion(m_block)); #else auto *userData = TextEditor::TextDocumentLayout::textUserData(m_block); if (!userData) { LOG_MESSAGE("RefactorSuggestionHoverHandler::operateTooltip: No user data in block"); return; } auto *suggestion = dynamic_cast(userData->suggestion()); #endif if (!suggestion) { LOG_MESSAGE("RefactorSuggestionHoverHandler::operateTooltip: No suggestion in block"); return; } LOG_MESSAGE("RefactorSuggestionHoverHandler::operateTooltip: Creating tooltip widget"); // Create compact widget with buttons auto *widget = new QWidget(); auto *layout = new QHBoxLayout(widget); layout->setContentsMargins(4, 3, 4, 3); layout->setSpacing(6); // Get theme colors const QColor normalBg = Utils::creatorColor(Utils::Theme::BackgroundColorNormal); const QColor hoverBg = Utils::creatorColor(Utils::Theme::BackgroundColorHover); const QColor selectedBg = Utils::creatorColor(Utils::Theme::BackgroundColorSelected); const QColor textColor = Utils::creatorColor(Utils::Theme::TextColorNormal); const QColor borderColor = Utils::creatorColor(Utils::Theme::SplitterColor); const QColor successColor = Utils::creatorColor(Utils::Theme::TextColorNormal); const QColor errorColor = Utils::creatorColor(Utils::Theme::TextColorError); auto *applyButton = new QPushButton("✓ Apply", widget); applyButton->setFocusPolicy(Qt::NoFocus); applyButton->setToolTip("Apply refactoring (Tab)"); applyButton->setCursor(Qt::PointingHandCursor); applyButton->setStyleSheet(QString( "QPushButton {" " background-color: %1;" " color: %2;" " border: 1px solid %3;" " border-radius: 3px;" " padding: 4px 12px;" " font-weight: bold;" " font-size: 11px;" " min-width: 60px;" "}" "QPushButton:hover {" " background-color: %4;" " border-color: %2;" "}" "QPushButton:pressed {" " background-color: %5;" "}") .arg(selectedBg.name()) .arg(successColor.name()) .arg(borderColor.name()) .arg(selectedBg.lighter(110).name()) .arg(selectedBg.darker(110).name())); QObject::connect(applyButton, &QPushButton::clicked, widget, [this]() { Utils::ToolTip::hide(); if (m_applyCallback) { m_applyCallback(); } }); auto *dismissButton = new QPushButton("✕ Dismiss", widget); dismissButton->setFocusPolicy(Qt::NoFocus); dismissButton->setToolTip("Dismiss refactoring (Esc)"); dismissButton->setCursor(Qt::PointingHandCursor); dismissButton->setStyleSheet(QString( "QPushButton {" " background-color: %1;" " color: %2;" " border: 1px solid %3;" " border-radius: 3px;" " padding: 4px 12px;" " font-size: 11px;" " min-width: 60px;" "}" "QPushButton:hover {" " background-color: %4;" " color: %5;" " border-color: %5;" "}" "QPushButton:pressed {" " background-color: %6;" "}") .arg(normalBg.name()) .arg(textColor.name()) .arg(borderColor.name()) .arg(hoverBg.name()) .arg(errorColor.name()) .arg(hoverBg.darker(110).name())); QObject::connect(dismissButton, &QPushButton::clicked, widget, [this]() { Utils::ToolTip::hide(); if (m_dismissCallback) { m_dismissCallback(); } }); layout->addWidget(applyButton); layout->addWidget(dismissButton); // Position tooltip above cursor, like standard Qt Creator suggestions const QRect cursorRect = editorWidget->cursorRect(editorWidget->textCursor()); QPoint pos = editorWidget->viewport()->mapToGlobal(cursorRect.topLeft()) - Utils::ToolTip::offsetFromPosition(); pos.ry() -= widget->sizeHint().height(); LOG_MESSAGE(QString("RefactorSuggestionHoverHandler::operateTooltip: Showing tooltip at (%1, %2)") .arg(pos.x()).arg(pos.y())); Utils::ToolTip::show(pos, widget, editorWidget); } } // namespace QodeAssist