Last active
January 4, 2026 20:46
-
-
Save maurges/73550862ae821c28c6276838df235405 to your computer and use it in GitHub Desktop.
Use qt-c++ to render a text, and know when the page ends
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| build/ | |
| compile_commands.json | |
| .cache/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <QApplication> | |
| #include <QLabel> | |
| #include "PaintWidget.h" | |
| int main(int argc, char *argv[]) { | |
| QApplication app(argc, argv); | |
| MainWindow window; | |
| window.show(); | |
| return app.exec(); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| SOURCES = src/main.cpp src/PaintWidget.cpp | |
| HEADERS = | |
| MOC_HEADERS = src/PaintWidget.h | |
| APP_NAME = example | |
| CXX = clang++ | |
| MOC = /usr/libexec/qt6/moc | |
| QT_CXXFLAGS = $(shell pkg-config --cflags Qt6Widgets Qt6Core) | |
| QT_LDFLAGS = $(shell pkg-config --libs Qt6Widgets Qt6Core) | |
| CXXFLAGS = -std=c++17 -Wall -Wextra -Wpedantic $(QT_CXXFLAGS) | |
| LDFLAGS = $(QT_LDFLAGS) | |
| # generate cpp files from moc-needing headers | |
| MOC_SOURCES = $(patsubst src/%.h,build/moc/moc-%.cpp,$(MOC_HEADERS)) | |
| # generate object files from cpp files | |
| OBJECTS = $(patsubst src/%.cpp,build/%.o,$(SOURCES)) \ | |
| $(patsubst build/moc/%.cpp,build/moc/%.o,$(MOC_SOURCES)) | |
| TARGET = build/$(APP_NAME) | |
| # default rule | |
| all: $(TARGET) | |
| # link all objects to target executable | |
| $(TARGET): $(OBJECTS) | |
| $(CXX) $(OBJECTS) -o $@ $(LDFLAGS) | |
| # moc for some headers | |
| build/moc/moc-%.cpp: src/%.h | build | |
| $(MOC) -o $@ $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(CPPFLAGS) $(QT_CXXFLAGS) $< | |
| # build object files | |
| build/%.o: src/%.cpp $(HEADERS) $(MOC_HEADERS) | build | |
| $(CXX) $(CXXFLAGS) -c $< -o $@ | |
| # special rule for moc object files | |
| build/moc/%.o: build/moc/%.cpp | build | |
| $(CXX) $(CXXFLAGS) -c $< -o $@ | |
| build: | |
| mkdir -p build/moc | |
| run: $(TARGET) | |
| ./$(TARGET) | |
| clean: | |
| rm -rf build | |
| .PHONY: all clean |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include "PaintWidget.h" | |
| #include <QTextLayout> | |
| static QString content = "The appearance of the heroine in gleaming power armor had brought the room to a hush. The silence only allowed Dragon’s words to carry, bouncing off the hard floor, reaching the assembled students and staff of Arcadia High.\n" | |
| "\n" | |
| "A low murmur ran through the room like an almost imperceptible aftershock, informing anyone and everyone who hadn’t been in earshot.\n" | |
| "\n" | |
| "I could see Emma too, or I could see glimpses of her, between the students that were backing away from the front of the room. Already pale in complexion, she was white, now, staring.\n" | |
| "\n" | |
| "I exhaled slowly, though my heart was pounding as if I’d just finished a hard run.\n" | |
| "\n" | |
| "Defiant advanced a step, with the door to the kitchens behind him, while I took a few steps back toward the rest of the cafeteria, putting both Dragon and Defiant in front of me. Some of my bugs flowed in through the gaps around the door he’d rammed through. He’d slammed it shut behind him, but the metal had twisted around the lock, giving smaller bugs a path.\n" | |
| "\n" | |
| "He slammed his spear against the ground. The entire cafeteria flinched at the crackle of electricity that ripped through the air around him, flowing along exposed pipe and the heating ducts in a path to the door. Every bug in the hallway died.\n" | |
| "\n" | |
| "No use bringing bugs in that way.\n" | |
| "\n" | |
| "I looked around me. This wasn’t an optimal battlefield. There were counters all around me, limiting my mobility, while barely impacting theirs. Someone had signaled Kid Win, Clockblocker and Adamant. The three heroes were heading our way. Sere remained tied up outside.\n" | |
| "\n" | |
| "Five capes against me. With the bugs that had flowed into the building with Kid Win, I had maybe a thousand flying insects and some spiders. Not nearly enough to mount an offensive. I had neither a weapon nor swarm to give me an edge. I didn’t have my costume, either, but that wasn’t liable to matter.\n" | |
| "\n" | |
| "Once upon a time, I’d had trouble getting my head around what Grue had been saying about reputation, about image and conveying the right impressions. Now it was all I had.\n" | |
| "\n" | |
| "I let out another slow breath. Calm down. I rolled my shoulders, letting the kinks out. There was something almost relieving about the idea that things couldn’t get much worse than they were right now. Let the tension drain out. If they decided to drag me off to jail or the Birdcage, there wasn’t anything I could do about it.\n" | |
| "\n" | |
| "They weren’t attacking. Maybe it wasn’t as bad as I thought. Were they not here to arrest me, or were they covering major routes my bugs might travel, to minimize my offensive strength?\n" | |
| "\n" | |
| "Or did I have leverage I wasn’t accounting for?\n" | |
| "\n" | |
| "I backed up until I’d reached a counter, then hopped up onto the edge, tucking one leg under me. It was a vantage point that gave me the ability to look directly at Dragon, with Defiant at the far left of my field of vision and many of the students to my right, Emma included.\n" | |
| "\n" | |
| "“Low blow, Dragon,” I said, finally. “Outing me? I thought you were better than that.”\n"; | |
| void PaintWidget::paintEvent(QPaintEvent *) { | |
| const int margin = 10; | |
| auto paragraphs = content.split("\n"); | |
| QPainter painter(this); | |
| painter.setRenderHint(QPainter::Antialiasing); | |
| painter.drawRect(0, 0, this->width(), this->height()); | |
| painter.drawRect(margin, margin, this->width() - 2 * margin, this->height() - 2 * margin); | |
| QFontMetrics fontMetrics = painter.fontMetrics(); | |
| int fontLeading = fontMetrics.leading(); | |
| // Top of the current line | |
| int y = 0; | |
| // Minus margin at the top and the bottom | |
| int textBoxHeight = this->height() - 2 * margin; | |
| int textBoxWidth = this->width() - 2 * margin; | |
| for (auto& paragraph : paragraphs) { | |
| auto textLayout = QTextLayout(paragraph, painter.font()); | |
| QTextOption textOption; | |
| textOption.setAlignment(Qt::AlignmentFlag::AlignJustify); | |
| textLayout.setTextOption(textOption); | |
| textLayout.beginLayout(); | |
| auto didOverfill = false; | |
| while (true) { | |
| auto line = textLayout.createLine(); | |
| if (!line.isValid()) { | |
| break; | |
| } | |
| line.setLineWidth(textBoxWidth); | |
| line.setPosition(QPoint(0, y)); | |
| // Advance line top | |
| y += line.height() + fontLeading; | |
| // We need to check if after advancing the next line we'll be out of | |
| // the frame, since this means the text got vertically clipped | |
| if (textBoxHeight < y + line.height() + fontLeading) { | |
| // Break out of both loops | |
| didOverfill = true; | |
| break; | |
| } | |
| } | |
| textLayout.endLayout(); | |
| textLayout.draw(&painter, QPoint(margin, margin)); | |
| if (didOverfill) { | |
| break; | |
| } | |
| } | |
| } | |
| void PaintWidget::cycleColor() | |
| { | |
| lineColor = (lineColor == Qt::black) ? Qt::red : Qt::black; | |
| update(); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #pragma once | |
| #include <QApplication> | |
| #include <QWidget> | |
| #include <QPainter> | |
| #include <QPushButton> | |
| #include <QVBoxLayout> | |
| class PaintWidget : public QWidget | |
| { | |
| Q_OBJECT | |
| public: | |
| inline PaintWidget(QWidget *parent = nullptr) | |
| : QWidget(parent) | |
| , lineColor(Qt::black) | |
| { | |
| setMinimumSize(200, 200); | |
| } | |
| void cycleColor(); | |
| protected: | |
| void paintEvent(QPaintEvent *event) override; | |
| private: | |
| QColor lineColor; | |
| }; | |
| class MainWindow : public QWidget | |
| { | |
| Q_OBJECT | |
| public: | |
| inline MainWindow(QWidget *parent = nullptr) | |
| : QWidget(parent) | |
| , paintWidget(new PaintWidget(this)) | |
| { | |
| QPushButton *button = new QPushButton("Cycle Color", this); | |
| QVBoxLayout *layout = new QVBoxLayout(this); | |
| layout->addWidget(paintWidget, 1); | |
| layout->addWidget(button); | |
| connect(button, &QPushButton::clicked, paintWidget, &PaintWidget::cycleColor); | |
| setWindowTitle("Qt Diagonal Line Painter"); | |
| resize(400, 400); | |
| } | |
| private: | |
| PaintWidget *paintWidget; | |
| }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment