Прокрутка окна ncurses не работает, даже если для scrollok установлено значение true - PullRequest
0 голосов
/ 30 декабря 2018

Я пишу файловый менеджер ncurses на C с пользовательским интерфейсом, подобным файловому менеджеру ranger.Вот мой код:

#include <stdio.h>
#include <dirent.h>
#include <curses.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pwd.h>


/*
    Creates a new window with dimensions `height` and `width` starting at `starty` and `startx`
*/
WINDOW *create_newwin(int height, int width, int starty, int startx)
{   
    WINDOW *local_win;

    local_win = newwin(height, width, starty, startx);

    return local_win;
}


/*
    Returns number of files in `char* directory`
*/
int getNumberofFiles(char* directory)
{
    int len=0;
    DIR *pDir;
    struct dirent *pDirent;

    pDir = opendir (directory);
    if (pDir == NULL) {
        printf ("Cannot open directory '%s'\n", directory);
        exit(0);
    }

    while ((pDirent = readdir(pDir)) != NULL) {
        // Skip hidden files
      if(pDirent->d_name[0] != '.' )
        len++;
    }
    return len;
}


/*
    Stores all the file names in `char* directory` to `char *target[]`
*/
void getFiles(char* directory, char* target[])
{
    int i = 0;
    DIR *pDir;
    struct dirent *pDirent;

    pDir = opendir (directory);
    if (pDir == NULL) {
        printf ("Cannot open directory '%s'\n", directory);
        exit(0);
    }

    while ((pDirent = readdir(pDir)) != NULL) {
        // Skip hidden files
        if(pDirent->d_name[0] != '.')
          target[i++] = strdup(pDirent->d_name);
    }

    closedir (pDir);
}


int main(int argc, char* argv[])
{
    // To store number of files in directory
    int len=0;
    // Counter variable
    int i = 0;
    // Direcotry to be opened
    char* dir;

    // Get UID of user
    uid_t uid = getuid();
    // Get home directory of user from UID
    struct passwd *info = getpwuid(uid);

    // No Path is given in arguments
    // Set Path as $HOME
    if(argc == 1)
    {
        dir = info->pw_dir;
    }

    // Path is given in arguments
    // Set Path as the argument
    else if(argc == 2)
    {
        dir = argv[1];

        // Relative Path Given
        if(dir[0] != '/')
        {
            // Add path of $HOME before the Relative Path
            char temp[250] = "";
            strcat(temp,info->pw_dir);
            strcat(temp,"/");
            strcat(temp,dir);
            dir = temp;
        }   
    }
    // Incorrect Useage
    else
    {
        printf("Incorrect Useage\n");
        exit(0);
    }

    // Get number of files in the home directory
    len = getNumberofFiles(dir);

    // ncurses initialization
    initscr();
    raw();
    noecho();
    curs_set(0);

    // Shows current directory
    WINDOW *current_win;
    // Shows child directory preview
    WINDOW *preview_win;
    int startx, starty, midx, midy, maxx, maxy;

    // Index of currently selected item in `char* directories`
    int selection = 0;
    char ch;
    do 
    {
        len = getNumberofFiles(dir);
        char* directories[len];
        getFiles(dir, directories);

        getmaxyx(stdscr, maxy, maxx);

        // Make the two windows side-by-side
        current_win = create_newwin(maxy, maxx/2+3, 0, 0);
        preview_win = create_newwin(maxy, maxx/2 -1, 0, maxx/2 + 1);

        // Print all the elements and highlight the selection
        for( i=0; i<len; i++ )
        {
            if(i==selection)
                wattron(current_win, A_STANDOUT);
            else
                wattroff(current_win, A_STANDOUT);
            wmove(current_win,i+1,2);
            wprintw(current_win, "%s\n", directories[i]);
        }

        char* selected_file = directories[selection];
        char next_dir[250] = "";
        char prev_dir[250] = "";
        char *p;

        // Get path of parent directory
        strcat(prev_dir, dir);
        p = strrchr(dir,'/');
        prev_dir[p-dir] = '\0';

        // Parent directory is root
        if(prev_dir[0] != '/')
            prev_dir[0] = '/';

        // Get path of child directory
        strcat(next_dir, dir);
        strcat(next_dir, "/");
        strcat(next_dir, directories[selection]);
        int len_preview = getNumberofFiles(next_dir);
        char* next_directories[len_preview];
        getFiles(next_dir, next_directories);

        for( i=0; i<len_preview; i++ )
        {
            wmove(preview_win,i+1,2);
            wprintw(preview_win, "%s\n", next_directories[i]);
        }

        wattroff(current_win, A_STANDOUT);
        box(current_win,0,0);
        box(preview_win,0,0);
        wrefresh(current_win);
        wrefresh(preview_win);

        // Keybindings
        switch( ch = getch() ) {
            case 'k':
                selection--;
                selection = ( selection < 0 ) ? len-1 : selection;
                break;
            case 'j':
                selection++;
                selection = ( selection > len-1 ) ? 0 : selection;
                break;
            case 'l':
                strcpy(dir, next_dir);
                selection = 0;
                break;
            case 'h':
                strcpy(dir, prev_dir);
                selection = 0;
                break;
            case 'g':
                selection = 0;
                break;
            case 'G':
                selection = len-1;
                break;
        }

        // Free Memory
        for( i=0; i<len_preview; i++ )
        {
            free(next_directories[i]);
        }

        for( i=0; i<len; i++ )
        {
            free(directories[i]);
        }
    } while( ch != 'q');

    endwin();


    return 0;
}

По сути, есть два окна, отображаемых рядом.Один - current_win, который показывает все файлы в текущем каталоге, а другой - preview_win, который показывает все файлы в дочернем каталоге выбранного каталога.

Проблема в том, что в текущем каталоге многофайлов, окна не прокручиваются, когда выделение выходит за пределы окна.Я попытался scrollok(current_win, TRUE);, но он автоматически прокручивается в нижний правый угол и остается таким же образом.

Как это работает?

(также по какой-то причине оболочка остается пустой после открытияскомпилированный двоичный файл до нажатия клавиши)

Ответы [ 2 ]

0 голосов
/ 31 декабря 2018

При чтении примера этот блок имеет проблему:

    for( i=0; i<len; i++ )
    {
        if(i==selection)
            wattron(current_win, A_STANDOUT);
        else
            wattroff(current_win, A_STANDOUT);
        wmove(current_win,i+1,2);
        wprintw(current_win, "%s\n", directories[i]);
    }

Если значение len больше, чем количество строк в окне, *Вызов 1008 * не удастся - оставив курсор там, где его поместит вызов wprintw.Без включения scrollok это будет правая нижняя часть окна.Но это (с включенным scrollok) это все равно будет последняя строка current_win (в этом случае внизу слева).Если вы уберете вызовы wmove и включите scrollok, результат будет больше, чем вы ожидаете.

Та же проблема существует в цикле для preview_win .

Вопрос в самом конце спрашивает о пустом экране: это потому, что в этом фрагменте

    wrefresh(current_win);
    wrefresh(preview_win);

    // Keybindings
    switch( ch = getch() ) {

getch делает

wrefresh(stdscr);

который переписывает окна, которые были только что нарисованы.Вы можете изменить это wgetch на

switch ( ch = wgetch(current_win) ) {

и stdscr не помешает.

0 голосов
/ 30 декабря 2018

Прокрутка, которую вы получаете в окне с scrollok, очень проста.Когда вы пишете после конца, он вставляет пустую строку внизу.Вот и все.Линия сверху исчезает?Это прошло.Не хранится в каком-то внеэкранном буфере.Ушел навсегда.Это не какая-то интерактивная, двунаправленная прокрутка.Он не знает, где находится ваш «выбор», и все равно ничего не может сделать с этой информацией.

Чтобы выполнить обратную прокрутку, вы можете использовать wscrl.Как и прокрутка вперед, она добавляет пустую строку (вверху).Вы несете ответственность за перерисовку информации, принадлежащей этой строке.

Возможно, будет проще использовать интерфейс более высокого уровня, curses pad .

Создать панельдостаточно большой, чтобы вместить полный список, и закрасить верхнюю часть с помощью prefresh.Когда выделение выходит за пределы видимой области, снова вызовите prefresh с обновленными координатами, чтобы сообщить ему, что должна отображаться часть пэда, включающая новый выделенный фрагмент.

...