Starting point to custom Krita frame

I didn’t know “Pureref” was also developed with C++ and Qt.
So I was wondering, why not try to imagine what I often requested
for the cosmetics of Krita’s UI, mainly a custom titlebar merged
with the software, in order to leave more space?

A - Inspiration :
There are two ways to go about it

A-1) Clip Studio way :
image

Pro :

  • you can still check the app name and document name on the titlebar

Con :

  • no app logo at the top left of the titlebar

A-2) Photoshop way( or some other apps ) :
image
image

Pros :

  • icon of the app at the top left on the titlebar.
  • the titlebar also carries drop down menues as well as
    the main window frame management buttons( minimize, maximize/restoredown, close )
  • even more space since the titlebar carries the dropdown
    menues

Con : no more document name or app name on the titlebar

B - Elaborating the Krita way( requires long term discussion
and progress )


This is currently what it looks like with Krita on Windows.
It’s not impossible to mix a bit of the two( pick the
photoshop way and add app name on the titlebar , but, the direction
for this might requires some vote and but also a more careful
and informed judgement. It is a matter of precaution I guess.

Nevertheless, we can have a starting point for the design of such
custom titlebar.

#include <QMainWindow>
#include <QFrame>
#include <QHBoxLayout>
#include <QPushButton>
#include <QApplication>
#include <QIcon>
#include <QMouseEvent>
#include <QDebug>

class CustomTitleBar : public QFrame {
    Q_OBJECT

public:
    CustomTitleBar(QWidget *parent = nullptr) : QFrame(parent) {
        // Set up the title bar appearance
        setObjectName("titleBar"); // For styling with CSS
        setFixedHeight(30); // Set a fixed height for the title bar
        setLayout(new QHBoxLayout(this));
        layout()->setContentsMargins(0, 0, 0, 0); // Remove margins

        // Add application icon (replace with your own)
        QLabel *iconLabel = new QLabel(this);
        iconLabel->setPixmap(QIcon(":/app_icon.png").pixmap(16, 16)); // Load your icon
        layout()->addWidget(iconLabel);

        // Add window title label
        titleLabel = new QLabel(this);
        titleLabel->setObjectName("titleLabel");  // For Styling
        layout()->addWidget(titleLabel);
        layout()->addStretch(1); // Stretch to push buttons to the right

        // Create minimize, maximize/restore, and close buttons
        minimizeButton = createButton(":/minimize.png");
        maximizeButton = createButton(":/maximize.png");
        closeButton = createButton(":/close.png");

        layout()->addWidget(minimizeButton);
        layout()->addWidget(maximizeButton);
        layout()->addWidget(closeButton);

        connect(minimizeButton, &QPushButton::clicked, this, &CustomTitleBar::minimizeWindow);
        connect(maximizeButton, &QPushButton::clicked, this, &CustomTitleBar::maximizeRestoreWindow);
        connect(closeButton, &QPushButton::clicked, this, &CustomTitleBar::closeWindow);

        // Make the title bar draggable
        setMouseTracking(true); // Important for dragging
    }

    void setTitle(const QString &title) {
        titleLabel->setText(title);
    }

protected:
    void mousePressEvent(QMouseEvent *event) override {
        if (event->button() == Qt::LeftButton) {
            m_dragging = true;
            m_dragStartPosition = event->pos();
        }
        QWidget::mousePressEvent(event);
    }


    void mouseMoveEvent(QMouseEvent *event) override {
        if (m_dragging) {
            QWidget *window = this->window();
            if (window) {
                window->move(window->pos() + event->pos() - m_dragStartPosition);
            }
        }
        QWidget::mouseMoveEvent(event);
    }

    void mouseReleaseEvent(QMouseEvent *event) override {
        if (event->button() == Qt::LeftButton) {
            m_dragging = false;
        }
        QWidget::mouseReleaseEvent(event);
    }


private:
    QPushButton *createButton(const QString &iconPath) {
        QPushButton *button = new QPushButton(this);
        button->setIcon(QIcon(iconPath));
        button->setFixedSize(20, 20); // Adjust size as needed
        button->setFlat(true); // Remove button background
        return button;
    }

private slots:
    void minimizeWindow() {
        QWidget *window = this->window();
        if (window) {
            window->showMinimized();
        }
    }

    void maximizeRestoreWindow() {
        QWidget *window = this->window();
        if (window) {
            if (window->isMaximized()) {
                window->showNormal();
                maximizeButton->setIcon(QIcon(":/maximize.png")); // Change icon
            } else {
                window->showMaximized();
                maximizeButton->setIcon(QIcon(":/restore.png")); // Change icon
            }
        }
    }

    void closeWindow() {
        QWidget *window = this->window();
        if (window) {
            window->close();
        }
    }

private:
    QLabel *titleLabel;
    QPushButton *minimizeButton;
    QPushButton *maximizeButton;
    QPushButton *closeButton;
    bool m_dragging = false;
    QPoint m_dragStartPosition;

};


class MainWindow : public QMainWindow {
public:
    MainWindow() {
        // Remove the default title bar
        setWindowFlags(Qt::FramelessWindowHint);

        // Create the custom title bar
        customTitleBar = new CustomTitleBar(this);
        setMenuWidget(customTitleBar); // Set the titlebar widget

        // Set the window title (will be displayed in the custom title bar)
        setWindowTitle("My Custom Window");
        customTitleBar->setTitle(windowTitle()); // Set the title in the custom title bar

        // ... rest of your main window setup ...
        // Example: Add a central widget
        QWidget *centralWidget = new QWidget(this);
        setCentralWidget(centralWidget);

        // Styling (QSS) - Add this to your main.cpp or a separate .qss file
        QString styleSheet = R"(
            #titleBar {
                background-color: #3498db; /* Example color */
                color: white;
            }
            #titleLabel {
                font-weight: bold;
                margin-left: 5px; /* Add some left margin */
            }

            #titleBar QPushButton {
                background: transparent; /* Make button background transparent */
            }
            #titleBar QPushButton:hover {
                background-color: rgba(255, 255, 255, 0.2); /* Hover effect */
            }
        )";
        setStyleSheet(styleSheet);
    }

private:
    CustomTitleBar *customTitleBar;
};

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

#include "main.moc" // Important for signals and slots

A bit of explaining :

  • Frameless Window: Qt::FramelessWindowHint removes the default window frame.
  • Custom Title Bar Widget: The CustomTitleBar class is now a separate widget, making the code more organized.
  • Layout: Uses QHBoxLayout for proper arrangement of title bar elements.
  • Application Icon: Added a placeholder for the application icon. Remember to replace ":/app_icon.png" with the actual path to your icon file. You’ll need to add the icon to your Qt resource file (.qrc).
  • Window Title: The setTitle function sets the text of the title label. It’s called initially and you can call it whenever the window title changes.
  • Minimize, Maximize/Restore, Close: The button clicks now call the appropriate window functions (showMinimized, showMaximized/showNormal, close). The maximize button’s icon is toggled between maximize and restore.
  • Draggable Title Bar: Implemented mouse press, move, and release events to allow dragging the window by the title bar.
  • Styling with QSS: Added a basic example of styling the title bar using Qt Style Sheets (QSS). This is how you’ll customize the appearance (colors, fonts, etc.). Put this in your main.cpp or in a separate .qss file that you load.
  • Resource File: You’ll need to create a Qt resource file (.qrc) to include your icon files. This is how Qt manages assets.
  • Signal/Slot Connection: The connect statements link button clicks to the corresponding functions.
  • moc Include: The #include "main.moc" is essential for Qt’s meta-object system, which is used for signals and slots. Make sure it’s present in your main.cpp file.

So there we have it.
Haven’t built and run from this code yet.
Not really familiar with QSS but it seems to be responsible
for the visual appearance of the UI. Frontend?

Feel free to share your thoughts about this.
Even if some R&D is done on that side in the lab,
I realistically do not see any implementation happening anytime soon.
As a lot of important things are still undergoing with Krita’s development.

1 Like