// gcc `pkg-config --cflags gtk+-3.0` main.c `pkg-config --libs gtk+-3.0 cairo`
#include <gtk/gtk.h>
#include <cairo.h>
#include <stdlib.h>
#include <string.h>
#define MAX_LINE_LENGTH 1024
#define LINE_SPACING 5
#define FONT_SIZE 20
typedef struct {
char **lines;
int line_count;
int max_line_length;
} AppData;
static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr, AppData *data) {
cairo_select_font_face(cr, "Monospace", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
cairo_set_font_size(cr, FONT_SIZE);
cairo_set_source_rgb(cr, 0, 0, 0);
for (int i = 0; i < data->line_count; i++) {
cairo_move_to(cr, 0, FONT_SIZE + i * (FONT_SIZE + LINE_SPACING));
cairo_show_text(cr, data->lines[i]);
}
return FALSE;
}
static void on_destroy(GtkWidget *widget, gpointer data) {
gtk_main_quit();
}
int main(int argc, char *argv[]) {
if (argc < 2) {
g_printerr("Usage: %s <filename>\n", argv[0]);
return EXIT_FAILURE;
}
gtk_init(&argc, &argv);
FILE *file = fopen(argv[1], "r");
if (!file) {
g_printerr("Could not open file: %s\n", argv[1]);
return EXIT_FAILURE;
}
AppData data;
data.lines = NULL;
data.line_count = 0;
data.max_line_length = 0;
char line[MAX_LINE_LENGTH];
while (fgets(line, sizeof(line), file)) {
data.lines = realloc(data.lines, sizeof(char *) * (data.line_count + 1));
if (data.lines == NULL) {
g_printerr("Memory allocation error.\n");
fclose(file);
return EXIT_FAILURE;
}
data.lines[data.line_count] = strdup(line);
if (strlen(line) > data.max_line_length) {
data.max_line_length = strlen(line);
}
data.line_count++;
}
fclose(file);
GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "Text Renderer");
gtk_window_set_default_size(GTK_WINDOW(window), 400, 300);
gtk_window_set_decorated(GTK_WINDOW(window), TRUE);
g_signal_connect(window, "destroy", G_CALLBACK(on_destroy), NULL);
GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
GtkWidget *drawing_area = gtk_drawing_area_new();
int height = data.line_count * (FONT_SIZE + LINE_SPACING);
int width = data.max_line_length * (FONT_SIZE / 2);
gtk_widget_set_size_request(drawing_area, width, height);
gtk_widget_set_margin_top(drawing_area, 0);
gtk_widget_set_margin_bottom(drawing_area, 0);
gtk_widget_set_margin_start(drawing_area, 0);
gtk_widget_set_margin_end(drawing_area, 0);
gtk_container_add(GTK_CONTAINER(scrolled_window), drawing_area);
gtk_container_add(GTK_CONTAINER(window), scrolled_window);
g_signal_connect(G_OBJECT(drawing_area), "draw", G_CALLBACK(on_draw_event), &data);
gtk_widget_show_all(window);
gtk_main();
return EXIT_SUCCESS;
}
在滚动时,这在区区 2M 文本文件下执行得非常糟糕
是的,我知道 Gtk.TextView 存在。 Gtk 文本小部件对于我想要做的事情来说是有限的(像文本编辑器这样的项目,具有多个光标、终端模拟器(我也知道 vte 存在)等奇特功能),而且我只是出于教育原因而想这样做。
我想专注于提高渲染性能,而不是从磁盘读取文件性能(因为,我的假设 - 渲染是主要问题)。
第一个想法是在最接近查看区域的块中渲染文本,我不知道这是否可能。
另一个想法是使用opengl而不是cairo来渲染它,我认为这不会有太大帮助。
这是我用来生成随机文本文件以测试滚动性能的命令:
dd if=/dev/urandom bs=1 count=2M | tr -dc '[:print:]\n' > random_text.txt
如有任何建议,我们将不胜感激:)
是的,我知道 Gtk.TextView 存在。
您是否查看过它的实现以寻找想法?
我发现了
gtk_widget_set_overflow(widget, GTK_OVERFLOW_HIDDEN);
。我不清楚它的作用,但听起来它可能会有所帮助。
除此之外……这似乎是个坏主意。我什至不明白它是如何绘制任何东西的。
我做的另一件事是看
Gtk.ScrolledWindow
。它的文档说它对可滚动小部件有特殊支持:
直接添加具有本机滚动支持的小部件,即那些类实现了 GtkScrollable 接口的小部件。对于其他类型的小部件,GtkViewport 类充当适配器,为其他小部件提供可滚动性。
Gtk.TextView 实现了 GtkScrollable,所以...也许这是用来不绘制屏幕外部分的?
第一个想法是在最接近查看区域的块中渲染文本,我不知道这是否可能。
这听起来是个好主意。 :-)
在https://docs.gtk.org/gtk3/class.DrawingArea.html,我发现:
并且该绘图被隐式剪切到暴露区域。
因此,您也许可以使用
cairo_clip_extents()
获取剪辑区域的边界框,然后“执行某些操作”以仅在此处绘制。您的绘图代码似乎知道每条线的位置及其大小(FONT_SIZE
,LINE_SPACING
)。
另一个想法是使用opengl而不是cairo来渲染它,我认为这不会有太大帮助。
是的...直觉,但我也不这么认为。
免责声明:我真的不太清楚我在说什么。我主要是猜测。