You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用ncurses C创建滚动控件:滚动时文本消失问题求助

解决ncurses滚动时文本消失的问题

问题根源

  1. 超出窗口的文本未被存储:你的load_file函数直接将文本绘制到窗口中,但窗口仅能保存当前可视范围内的内容,超出窗口高度的文本会被mvwprintw直接忽略,根本没有写入窗口缓冲区,所以滚动时自然没有内容可以显示。
  2. 滚动区域设置错误:wsetscrreg(win, 0, start_y)的结束行参数超出了窗口实际行范围。你创建的窗口高度为start_y,行索引是从0到start_y-1,且窗口带有边框,内容区域应为1到start_y-2,错误的滚动区域会导致滚动行为异常。

简化解决方案

无需复杂的逐行处理逻辑,只需预先缓存文件的所有行,再根据滚动位置重新绘制窗口内容即可。以下是修改后的代码:

#include "window.h"
#include <curses.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 缓存文件内容的动态数组
char **file_lines = NULL;
int line_count = 0;

WINDOW *new_window(char *file)
{
    WINDOW *win;
    int std_y, std_x;

    getmaxyx(stdscr, std_y, std_x);
    // 创建内容窗口,去掉边框占用的空间
    win = newwin(std_y - 2, std_x - 4, 1, 2);
    mvprintw(0, 2, "%s", file); // 标题放在窗口上方
    scrollok(win, TRUE);
    // 设置滚动区域为内容窗口的全部行
    int win_y, win_x;
    getmaxyx(win, win_y, win_x);
    wsetscrreg(win, 0, win_y - 1);
    // 在stdscr绘制外层边框
    box(stdscr, 0, 0);
    wrefresh(win);
    refresh();
    return win;
}

void init_window()
{
    initscr();
    cbreak();
    noecho();
    start_color();
    curs_set(0);
    init_pair(1, COLOR_WHITE, COLOR_BLACK);
    bkgd(COLOR_PAIR(1));
    refresh();
}

void end_window(WINDOW *win)
{
    // 释放缓存的文件行
    for (int i = 0; i < line_count; i++) {
        free(file_lines[i]);
    }
    free(file_lines);
    delwin(win);
    endwin();
}

// 读取文件到缓存数组,自动拆分超长行
void load_file_to_buffer(char *path, int max_x)
{
    FILE *f = fopen(path, "r");
    if (f == NULL) {
        return;
    }

    char buf[1024];
    while (fgets(buf, sizeof(buf), f) != NULL) {
        // 去掉换行符
        buf[strcspn(buf, "\n")] = '\0';
        int len = strlen(buf);
        int pos = 0;
        // 拆分超长行为适配窗口宽度的多行
        while (pos < len) {
            int chunk_len = (len - pos) > max_x - 1 ? max_x - 1 : len - pos;
            char *line = malloc(chunk_len + 1);
            strncpy(line, buf + pos, chunk_len);
            line[chunk_len] = '\0';
            // 加入动态数组
            file_lines = realloc(file_lines, sizeof(char*) * (line_count + 1));
            file_lines[line_count++] = line;
            pos += chunk_len;
        }
    }
    fclose(f);
}

// 根据滚动位置绘制窗口内容
void draw_window_content(WINDOW *win, int scroll_pos)
{
    int win_y, win_x;
    getmaxyx(win, win_y, win_x);
    wclear(win);

    // 绘制当前可见范围内的行
    for (int i = 0; i < win_y; i++) {
        int line_idx = scroll_pos + i;
        if (line_idx < line_count) {
            mvwprintw(win, i, 0, "%s", file_lines[line_idx]);
        }
    }
    wrefresh(win);
}

int main(int argc, char **argv)
{
    if (argc < 2) {
        printf("Usage: %s <file>\n", argv[0]);
        return 1;
    }

    WINDOW *win;
    int win_y, win_x;
    int scroll_pos = 0;
    char ch;

    init_window();
    win = new_window(argv[1]);
    getmaxyx(win, win_y, win_x);

    load_file_to_buffer(argv[1], win_x);

    // 处理文件加载失败的情况
    if (file_lines == NULL) {
        mvwprintw(win, 0, 0, "Error: File not found or cannot be opened.");
        wrefresh(win);
        wgetch(win);
        end_window(win);
        return 1;
    }

    draw_window_content(win, scroll_pos);

    while ((ch = wgetch(win)) != 'q') {
        if (ch == 'j' && scroll_pos + win_y < line_count) {
            scroll_pos++;
            draw_window_content(win, scroll_pos);
        }
        if (ch == 'k' && scroll_pos > 0) {
            scroll_pos--;
            draw_window_content(win, scroll_pos);
        }
    }

    end_window(win);
    return 0;
}

修改说明

  1. 文件内容缓存:新增load_file_to_buffer函数,将文件内容拆分为适配窗口宽度的行,存储在动态数组中,确保所有内容都被保留。
  2. 窗口结构调整:将边框绘制在stdscr上,单独创建内容窗口,避免滚动时影响边框,同时简化滚动区域的设置。
  3. 滚动逻辑优化:通过scroll_pos变量跟踪当前滚动位置,每次按键后重新绘制对应范围的内容,确保滚动时显示正确的文本。
  4. 资源释放:程序退出时释放缓存的文件行,避免内存泄漏。

内容的提问来源于stack exchange,提问作者Apesteguia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 19:23:18