Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Элемент управления Grid… продолжение 1

Anna | 24.06.2014 | нет комментариев

В первом посте мы разбили функциональность грида на несколько классов. Давайте еще раз быстренько их опишем:

  • Lines — представляет комплект колонок либо строк.
  • Range — oписывает всякую общность ячеек.
  • Layout — разрешает размещать данные внутри ячейки.
  • Model — определяет интерфейс доступа к данным для View и Controller.
  • View — показывает информацию в ячейке.
  • Controller — разрешает пользователю менять данные.
  • CacheCell — кеширует данные для видимой ячейки.
  • CacheGrid — кеширует данные для видимой части грида.
  • GridWindow — особый контрол.

Так же мы описали модели и вью для текстовых данных (ModelText, ModelTextCallback. ViewText). Давайте испробуем сделать грид и привязать к нему текстовые данные. Новую функциональность, которая необходима для работы стандартного грида будем добавлять в виде особых Model/View/Controller.

Для краткости буду спускать в коде указатели, shared_ptr и т.п. Выходит, поехали…

int main()
{
    Application app;

    // создаем окно
    GridWindow grid_window("Demo");

    // создаем модель текста
    ModelTextCallback m;
    m.getCallback = [](CellID cell)->String {
        return String.Format("Item[%d, %d]", cell.row, cell.column);
    };
    // указываем гриду где, что и как рисовать
    grid_window.AddData(RangeAll(), ViewText(m), LayoutAll());

    // выставляем число строк и столбцов
    grid_window.SetRows(10);
    grid_window.SetColumns(10);
    // показываем окно
    grid_window.Show(100, 100, 300, 300);

    app.run();
}

Недурно, только хочется как-то обособлять ячейки: рисовать либо через-полосицу, либо сетку. Давайте реализуем один, а потом иной вариант. Верю код говорит сам за себя.

class ViewOddRowBackground: public View
{
public:
    void Draw(DrawContext& dc, Rect rect, CellID cell) const override
    {
        // закрасить ячейку, если строка чётная
        if (cell.row%2 == 0)
            dc.FillRect(rect, ColorRGB(200, 200, 255));
    }
};
void plugOddRowBackgroud(Grid& grid)
{
    grid.AddData(RangeAll(), ViewOddRowBackground(), LayoutAll(Transparent));
}

Подметьте, мы в LayoutAll передали параметр Transparent, он говорит о том, что данный layout не будет модифицировать прямоугольник ячейки. Помните, что по умолчанию LayoutAll «забирает» всю свободную область ячейки и зануляет её. В режиме Transparent, он этого делать не будет и дальнейший за ним ViewText получит тот же подлинный прямоугольник.
Осталось добавить вызов новой функции в main

    GridWindow grid_window("Demo");
    plugOddRowBackgroud(grid_window.GetGrid());

Сейчас реализуем сетку.

class ViewCellBounds: public View
{
public:
    void Draw(DrawContext& dc, Rect rect, CellID cell) const override
    {
        // нарисовать линию внизу ячейки
        dc.Line(rect.BottomLeft(), rect.BottomRight());
        // нарисовать линию у правого края ячейки
        dc.Line(rect.TopRight(), rect.BottomRight());
    }
};
void plugCellBounds(Grid& grid)
{
    grid.AddData(RangeAll(), ViewCellBounds(), LayoutAll(Transparent));
}
    ...
    GridWindow grid_window("Demo");
    plugCellBounds(grid_window.GetGrid());

Дальнейшим всеобщим местом для всех гридов является представление выделенных ячеек — selection. Предыдущие представления (views) не хранили никаких данных, следственно модели для них мы не создавали. Тут обстановка немножко труднее. Что же должно входить в ModelSelection? Во-первых, комплект выделенных ячеек, а, во-вторых, координаты энергичной ячейки (это та ячейка, которая традиционно выделена рамочкой, и с поддержкой клавиатуры мы трудимся именно с этой ячейкой). Пишем код:

class ModelSelection: public Model
{
public:
    Range GetSelectedRange() const { return m_selected_range; }
    void SetSelectedRange(Range new_selected_range)
    {
        m_selected_range = new_selected_range;
        changed.invoke(*this);
    }

    CellID GetActiveCell() const { return m_active_cell; }
    void SetActiveCell(CellID new_active_cell)
    {
        m_active_cell = new_active_cell;
        changed.invoke(*this);
    }
private:
    Range m_selected_range;
    CellID m_active_cell;
};

class ViewSelection: public View
{
public:
    ViewSelection(ModelSelection selection)
        : m_selection(selection)
    {}
    void Draw(DrawContext& dc, Rect rect, CellID cell) const override
    {
        // если ячейка энергичная -> рисуем рамку
        if (m_selection.GetActiveCell() == cell)
            dc.DrawFrame(rect);

        // если ячейка выделена -> закрашиваем область
        // и меняем нынешний цвет для текста
        if (m_selection.GetSelectedRange().HasCell(cell))
        {
            dc.FillRect(rect, ColorSelectedBackground);
            dc.SetTextColor(ColorSelectedText);
        }
    }
private:
    ModelSelection m_selection;
};

ModelSelection plugSelection(Grid& grid)
{
    ModelSelection selection;
    grid.AddData(RangeAll(), ViewSelection(selection), LayoutAll(Transparent));
    return selection;
}
    ...
    GridWindow grid_window("Demo");
    plugCellBounds(grid_window.GetGrid());
    plugSelection(grid_window.GetGrid());

Реализацию контроллера я спускаю ради экономии места. Поверьте, там тоже всё легко: по нажатию левой клавиши мыши меняем энергичную ячейку на ту, что под мышкой. При перемещении мыши и отпускании левой клавиши — создаем особый Range, тот, что описывает прямоугольный блок ячеек от энергичной (где мы зажали кнопку), до нынешней. Задаем данный Range в selection. Еще нужно рассматривать состояние клавиш Shift и Ctrl, но это детали. В выводе получаем следующую картинку.

Что бы дать вероятность пользователю менять размер строк и столбцов, нам необходимо реализовать особый контроллер, тот, что, по нажатию кнопки мышки около края ячейки, запомнит расположение мыши, а при отжатии мыши изменит ширину колонки на разницу между исходной точной и нынешней. Верю идея внятна. Стоит подметить, что контроллеры у нас «живут» в представлениях (views), следственно нам нужно сделать фальшивый View, тот, что ничего не рисует, а лишь определяем прямоугольник, в котором будет активизироваться контроллер.

Достаточно Зачастую гриды имеют заголовок — часть грида, которая неизменно остается в верхней части окна. Изредка есть схожая конструкция слева (традиционно там пишут номер строки). При скроллинге эти области остаются на своих местах. Давайте посмотрим, как заголовок может выглядеть у нас:

Дюже схоже на грид, состоящий из одной строки и такого же числа столбцов, что и подлинный грид. Тогда дозволено сказать, что заголовок — это грид, колонки которого синхронизированы с колонками подлинного грида (по сути в 2-х гридах применяется один и тот же экземпляр Lines для колонок). Данный грид размещен в верхней части окна и скроллируется только по горизонтали. Сходственное заявление дозволено сказать про левую фиксированную часть, только там синхронизируются строки. Таким образом у нас в классе GridWindow живут четыре CacheGrid взамен одного.

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

Выходит, класс GridWindow у нас усложнился — взамен одного CacheGrid, у нас возникло их девять штук. Функции отрисовки, скроллирования, обработки мышиных событий обязаны тоже усложниться. Я предлагаю тут остановиться и наблюдательно посмотреть на конечный рисунок. Издали он схож на область, разбитую на девять под-областей в три строки и три колонки. Схоже на грид с тремя строками и колонками, в ячейках которого отображаются другие гриды. По определению у нас View настоль многофункциональный, что может отображать всякую сущность. Так давайте сотворим View, тот, что в ячейке отображает определенный грид. Для нашего случая с девятью гридами мы получим приблизительно следующие классы:

class ModelGrid: public Model
{
public:
    ModelGrid();
    CacheGrid GetGrid(CellID cell) const { return m_grids[cell.row][cell.column]; }
private:
    CacheGrid m_grids[3][3];
};
class ViewGrid: public View
{
public:
    ViewGrid(ModelGrid model)
      :m_model(model)
    {}
    void Draw(DrawContext& dc, Rect rect, CellID cell) const override
    {
        CacheGrid cacheGrid  = m_model.GetGrid(cell);
        cacheGrid.Draw(dc);
    }
};

В конструкторе ModelGrid создаются девять объектов CacheGrid и синхронизируются строки и столбцы. Так же не сложно реализуется контроллер. Если мы добавим ViewGrid к нашему ветхому классу GridWindow, тот, что имел только один объект CacheGrid, то нам нет нужды создавать новейший тип GridWindow. Исключительное различие — позицию скроллбаров нам необходимо передавать в подгриды: игнорировать позицию скроллов будут угловые гриды (Top/Left, Top/Right, Botton/Right, Bottom/Left), по горизонтали будут скроллироваться Top и Bottom гриды, по вертикали — Left и Right. Ну а центральный Client грид будет скроллироваться по каждому сторонам. При этом код отрисовки и взаимодействия с мышь

 

Источник: programmingmaster.ru

 

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB