Современный порт Turbo Vision 2.0, классическая структура для текстовых пользовательских интерфейсов. Теперь кроссплатформенное и с поддержкой Unicode.

Я начал это как личный проект в самом конце 2018 года. К маю 2020 года я считал, что он очень близок, чтобы получить функцию с оригиналом, и решил сделать его открытым.
Первоначальные цели этого проекта были:
В какой -то момент я подумал, что сделал достаточно, и что любые попытки обновить библиотеку и преодоление ее первоначальных ограничений потребуют либо расширения API, либо прерывания назад совместимости, и что крупный переписывание, скорее всего, будет необходимым.
Однако в период с июля по август 2020 года я нашел способ интегрировать полноценную поддержку Unicode в существующую архитектуру, написал Turbo Text Editor, а также сделал новые функции, доступными в Windows. Поэтому я уверен, что Turbo Vision теперь может оправдать многие ожидания современных пользователей и программистов.
Оригинальное местоположение этого проекта - https://github.com/magiblot/tvision.
Многое изменилось с тех пор, как в начале 90 -х годов Борланд создал Turbo Vision. Многие инструменты GUI сегодня отделяют спецификацию внешнего вида от спецификации поведения, используют более безопасные или динамические языки, которые не связаны с ошибкой, и поддерживают ни параллельное, ни асинхронное программирование, ни оба.
Turbo Vision не преуспевает ни в одном из них, но это, безусловно, преодолевает многие из проблем, которые программисты по -прежнему сталкиваются сегодня при написании терминальных приложений:
Забудьте о возможностях терминала и прямого терминала ввода -вывода. При написании приложения Turbo Vision все, что вам нужно, это позаботиться, это то, что вы хотите, чтобы ваше приложение ведет себя и выглядел - нет необходимости добавлять обходные пути в ваш код. Turbo Vision изо всех сил старается получить одни и те же результаты во всех условиях. Например: чтобы получить яркий цвет фона на консоли Linux, необходимо установить атрибут Blink . Turbo Vision делает это для вас.
Повторно используйте то, что уже было сделано. Turbo Vision предоставляет множество классов виджетов (также известных как представления ), в том числе повторно используемые, перекрывающиеся окна, выпадающие меню, диалоговые окна, кнопки, полосы прокрутки, ящики ввода, флажки и радиопроизводительные кнопки. Вы можете использовать и расширить их; Но даже если вы предпочитаете создавать свое собственное, Turbo Vision уже обрабатывает отправку событий, отображение символов Unicode и т. Д.
Можете ли вы представить себе написание текстового интерфейса, который работает как на Linux, так и на Windows (и, следовательно, является кроссплатформенным) вне коробки, без #ifdef s? Turbo Vision делает это возможным. Во-первых, Turbo Vision продолжает использовать массивы char , а не полагаться на определяемую реализацией и зависящим от платформы wchar_t или TCHAR . Второе: благодаря поддержке UTF-8 в setlocale в недавних версиях Microsoft RTL, код, подобный следующему, будет работать как предполагалось:
std::ifstream f ( "コンピュータ.txt " ); // On Windows, the RTL converts this to the system encoding on-the-fly. Вы можете начать работу с Turbo Vision для руководства пользователя C ++ и посмотреть на образцы приложений hello , tvdemo и tvedit . Как только вы поймете основы, я предлагаю вам взглянуть на руководство по программированию Turbo Vision 2.0, которое, на мой взгляд, более интуитивно понятно и легче понять, несмотря на использование Pascal. К тому времени вы, вероятно, будут заинтересованы в примере palette , который содержит подробное описание того, как используются палитры.
Не забудьте также проверить разделы функций и API.
Этот проект пока не имеет стабильных выпусков. Если вы разработчик, постарайтесь придерживаться последнего коммита и сообщить о любых проблемах, которые вы найдете при обновлении.
Если вы просто хотите проверить демонстрационные приложения:
examples-dos32.zip : 32-битные исполняемые файлы, построенные с Borland C ++. Нет поддержки Unicode.examples-x86.zip : 32-битные исполняемые файлы, построенные с MSVC. Windows Vista или позже требуется.examples-x64.zip : 64-битные исполняемые файлы, построенные с MSVC. x64 Windows Vista или позже требуется. Turbo Vision может быть построена как статическая библиотека с Cmake и GCC/Clang.
cmake . -B ./build -DCMAKE_BUILD_TYPE=Release && # Could also be 'Debug', 'MinSizeRel' or 'RelWithDebInfo'.
cmake --build ./build # or `cd ./build; make` Версии Cmake старше 3,13 могут не поддерживать опцию -B . Вместо этого вы можете попробовать следующее:
mkdir -p build ; cd build
cmake .. -DCMAKE_BUILD_TYPE=Release &&
cmake --build .Вышеуказанное создает следующие файлы:
libtvision.a , которая является библиотекой турбовизионного видения.hello , tvdemo , tvedit , tvdir , которые были связаны с оригинальным турбо -видением (хотя некоторые из них имеют несколько улучшений).mmenu и palette от технической поддержки Борланда.tvhc , Turbo Vision помогает компилятору. Библиотека и исполняемые файлы можно найти в ./build .
Требования к сборке:
libncursesw (обратите внимание на «w»).libgpm для поддержки мыши на консоли Linux (необязательно). Если ваше распределение предоставляет отдельные пакеты Devel (например, libncurses-dev , libgpm-dev в дистрибуциях на основе Debian), установите их тоже.
Требования к выполнению:
xsel или xclip для поддержки буфера обмена в средах X11.wl-clipboard для поддержки буфера обмена в окружающей среде Wayland. Минимальная командная строка, необходимая для создания приложения Turbo Vision (например, hello.cpp с GCC) из корня этого проекта:
g++ -std=c++14 -o hello hello.cpp ./build/libtvision.a -Iinclude -lncursesw -lgpmВам также может понадобиться:
-Iinclude/tvision Если ваше приложение использует Turbo Vision 1.x включает ( #include <tv.h> вместо #include <tvision/tv.h> ).
-Iinclude/tvision/compat/borland , если ваше приложение включает в себя заголовки Borland ( dir.h , iostream.h и т. Д.).
На Gentoo (и, возможно, другие): -ltinfow если и libtinfo.so , так и libtinfow.so доступны в вашей системе. В противном случае вы можете получить ошибку сегментации при запуске приложений Turbo Vision (#11). Обратите внимание, что tinfo связан с ncurses .
-lgpm необходим только в том случае, если турбовидение было построено с поддержкой libgpm .
Заголовки с обратной совместимостью include/tvision/compat/borland подражая Borland C ++ RTL. Исходный код Turbo Vision по -прежнему зависит от них, и они могут быть полезны при переносе старых приложений. Это также означает, что включение tvision/tv.h принесет несколько имен std в глобальное пространство имен.
Процесс сборки с MSVC немного более сложный, так как есть больше вариантов на выбор. Обратите внимание, что вам понадобятся разные каталоги сборки для различных целевых архитектур. Например, чтобы генерировать оптимизированные двоичные файлы:
cmake . -B ./build && # Add '-A x64' (64-bit) or '-A Win32' (32-bit) to override the default platform.
cmake --build ./build --config Release # Could also be 'Debug', 'MinSizeRel' or 'RelWithDebInfo'. В приведенном выше примере tvision.lib и пример приложения будут размещены по адресу ./build/Release .
Если вы хотите связать Turbo Vision статически против библиотеки времени выполнения Microsoft ( /MT вместо /MD ), включите опцию TV_USE_STATIC_RTL ( -DTV_USE_STATIC_RTL=ON при вызове cmake ).
Если вы хотите связать приложение против Turbo Vision, обратите внимание, что MSVC не позволит вам смешать /MT с /MD или отладки с не-дебугскими двоичными файлами. Все компоненты должны быть связаны с RTL таким же образом.
Если вы разработаете собственное приложение Turbo Vision, обязательно включите следующие флаги компилятора, иначе вы получите ошибки компиляции при включении <tvision/tv.h> :
/permissive-
/Zc:__cplusplus
Если вы используете Turbo Vision в качестве подмодуля Cmake, эти флаги будут включены автоматически.
Примечание. Turbo Vision использует setlocale для установки функций RTL в режиме UTF-8. Это не сработает, если вы используете старую версию RTL.
С RTL, статически связанным, и если UTF-8 поддерживается в setlocale , приложения Turbo Vision переносимы и работают по умолчанию на Windows Vista, а затем .
После того, как ваша среда Mingw будет правильно настроена, сборка выполняется аналогичным образом Linux:
cmake . -B ./build -G " MinGW Makefiles " -DCMAKE_BUILD_TYPE=Release &&
cmake --build ./build В приведенном TV_BUILD_EXAMPLES примере libtvision.a и все примеры ON в ./build
Если вы хотите связать приложение против Turbo Vision, просто добавьте -L./build/lib -ltvision к вашему линкеру и -I./include в свой компилятор
Turbo Vision по -прежнему может быть построена в виде DOS или библиотеки Windows с Borland C ++. Очевидно, здесь нет поддержки Unicode.
Я могу подтвердить, что процесс сборки работает:
Вы можете столкнуться с разными проблемами в зависимости от вашей среды сборки. Например, Turbo Assembler нуждается в патче для работы под Windows 95. На Windows XP все, кажется, работает нормально. В Windows 10 Make может излучать ошибку Fatal: Command arguments too long , которые можно исправить путем модернизации создания в комплекте с Borland C ++ 5.x.
Да, это работает на 64-битной Windows 10. Что не сработает, так это установщик Borland C ++, который является 16-битным приложением. Вам придется запустить его в другой среде или попробовать удачу с WineVDM.
Borland Makefile можно найти в каталоге project . Сборка можно сделать, выполняя:
cd project
make.exe < options > Где <options> может быть:
-DDOS32 для 32-разрядных приложений DPMI (которые все еще работают на 64-битных Windows).-DWIN32 для 32-разрядных приложений Native Win32 (невозможно для TVDEMO, который опирается на farcoreleft() и другие древности).-DDEBUG для создания отладочных версий приложения и библиотеки.-DTVDEBUG для связи приложений с версией библиотеки отладки.-DOVERLAY , -DALIGNMENT={2,4} , -DEXCEPTION , -DNO_STREAMABLE , -DNOTASM для вещей, которые я никогда не использовал, но появлялся в оригинальных макияжах. Это составит библиотеку в каталог LIB рядом с project и будет компилировать исполняемые файлы для демонстрационных приложений в своих соответствующих examples/* каталогах.
Извините, корень Makefile предполагает, что он выполняется из каталога project . Вы все еще можете запустить оригинальные makefiles напрямую (в source/tvision и examples/* ), если вы хотите использовать разные настройки.
Turbo Vision может быть построено и установлено с помощью VCPKG Deginency Manager:
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
./vcpkg integrate install
./vcpkg install tvision Порт tvision в VCPKG обновляется членами команды Microsoft и участниками сообщества. Если вы обнаружите, что он устарел, пожалуйста, создайте проблему или запрос на вытягивание в репозитории VCPKG.
Если вы выберете систему сборки Cmake для вашего приложения, есть два основных способа связи с Turbo Vision:
Установка Turbo Vision и импорт с find_package . Установка зависит от типа генератора:
Сначала определите префикс установки. По умолчанию один будет работать вне коробки, но обычно требует привилегий администратора. В Systems Unix вы можете использовать $HOME/.local вместо этого. В Windows вы можете использовать любой пользовательский путь, который вы хотите, но вам придется добавить его в переменную среды CMAKE_PREFIX_PATH при создании вашего приложения.
Для генераторов моно-конфигурации ( Unix Makefiles , Ninja ...) вам нужно только построить и установить один раз:
cmake . -B ./build # '-DCMAKE_INSTALL_PREFIX=...' to override the install prefix.
cmake --build ./build
cmake --install ./build Для генераторов с несколькими конфигураторами ( Visual Studio , Ninja Multi-Config ...) Вы должны создать и установить все конфигурации:
cmake . -B ./build # '-DCMAKE_INSTALL_PREFIX=...' to override the install prefix.
cmake --build ./build --config Release
cmake --build ./build --config Debug --target tvision
cmake --build ./build --config RelWithDebInfo --target tvision
cmake --build ./build --config MinSizeRel --target tvision
cmake --install ./build --config Release
cmake --install ./build --config Debug --component library
cmake --install ./build --config RelWithDebInfo --component library
cmake --install ./build --config MinSizeRel --component library Затем в CMakeLists.txt в вашем приложении вы можете импортировать его так:
find_package (tvision CONFIG)
target_link_libraries (my_application tvision::tvision) Иметь турбовидение в подмодуле в вашем хранилище и импортируйте его с помощью add_subdirectory :
add_subdirectory (tvision) # Assuming Turbo Vision is in the 'tvision' directory.
target_link_libraries (my_application tvision) В любом случае, <tvision/tv.h> будет доступен в вашем приложении включать PATH во время компиляции, и ваше приложение будет связано с необходимыми библиотеками (NCURSES, GPM ...) автоматически.
tvedit .~/ in $HOME .stdin / stdout / stderr не мешает терминалу ввода -вывода.Есть несколько переменных среды, которые влияют на поведение всех приложений Turbo Vision:
TVISION_MAX_FPS : максимальная скорость обновления, по умолчанию 60 . Это может помочь сохранить плавность в эмуляторах терминалов с неэффективной обработкой символов, рисующих коробки. Специальными значениями для этой опции являются 0 , чтобы отключить ограничение скорости обновления и -1 , чтобы фактически привлечь к терминалу в каждом вызове THardwareInfo::screenWrite (полезно при отладке).
TVISION_CODEPAGE : набор символов, используемый внутри Turbo Vision для перевода расширенного ASCII в Unicode. Допустимые значения на данный момент составляют 437 и 850 , причем 437 - дефолт, хотя добавление больше требует очень мало усилий.
win32-input-mode (доступно в WSL).TIOCLINUX ) и мыши (через GPM) в консоли Linux.stderr является TTY, написанные ему сообщения перенаправляются в буфер, чтобы они не притягивали дисплей и в конечном итоге напечатаны на консоли при выходе или приостановке приложения.stderr , когда буфер будет заполнен. Если вы хотите сохранить весь stderr , просто перенаправьте его в файл из командной строки с 2> .Следующие переменные среды также принимаются во внимание:
TERM : Ncurses использует его для определения возможностей терминала. Он автоматически устанавливается эмулятором терминала.
COLORTERM : при установке на truecolor или 24bit , Turbo Vision предполагает, что эмулятор терминала поддерживает 24-разрядный цвет. Он автоматически устанавливается эмуляторами терминалов, которые поддерживают его.
ESCDELAY : количество миллисекундов, чтобы подождать после получения нажатия клавиши ESC, по умолчанию 10 . Если во время этой задержки нажимается другая клавиша, она будет интерпретирована как комбинация клавиш Alt+. Использование большего значения полезно, когда терминал не поддерживает ключ ALT.
TVISION_USE_STDIO : когда не пуст, терминал ввода / вывода выполняется через stdin / stdout , чтобы его можно было перенаправить из оболочки. По умолчанию Turbo Vision выполняет терминал ввода/вывода через /dev/tty , позволяя пользователю перенаправить stdin , stdout и stderr для их потребностей, не влияя на стабильность приложения.
Например, следующее out.txt .
tvdemo | tee out.txt В то время как следующее будет сбрасывать все последовательности побега и текст, напечатанный приложением в out.txt :
TVISION_USE_STDIO=1 tvdemo | tee out.txtНиже не доступно при компиляции с Borland C ++:
wchar_t . Примечание. Turbo Vision записывает текст UTF-8 непосредственно в консоль Windows. Если консоль установлена в устаревшем режиме и используется шрифт растрового изображения, символы Unicode не будут отображаться должным образом (фото). Чтобы избежать этого, Turbo Vision обнаруживает эту ситуацию и пытается изменить консольный шрифт на Consolas или Lucida Console .
Ниже приведены новые функции, недоступные в выпуске Borland Turbo Vision или в предыдущих портах с открытым исходным кодом (Sigala, Set):
TInputLine S больше не прокручивает текстовый дисплей на фокусировке/невоке, что позволяет соответствующему тексту оставаться видимым.TFileViewer ( tvdemo ) и TEditor ( tvedit ). TEditor сохраняет строку, заканчивающуюся сохранением файла, но все вновь созданные файлы по умолчанию используют CRLF.TEditor : Контекстное меню на щелчке правой кнопки.TEditor : Перетащите свиток с помощью средней кнопки мыши.TEditor , TInputLine : удалить целые слова с помощью kbAltBack , kbCtrlBack и kbCtrlDel .TEditor : Ключ на дому переключается между началом линии и началом стуческого текста.TEditor : Поддержка файлов больше 64 киб на 32-битных или 64-битных сборках.tvdemo : Applet Event Viewer Applet Полезно для отладки событий.tvdemo : опция изменить фоновый шаблон. Экранные записи буферизируются и обычно отправляются на терминал один раз для каждой итерации цикла активного события (см. Также TVISION_MAX_FPS ). Если вам нужно обновить экран во время занятого цикла, вы можете использовать TScreen::flushScreen() .
TDrawBuffer больше не является массивом с фиксированной длиной, и его методы предотвращают доступ на прошедшие кран. Следовательно, старый код, содержащий сравнения с sizeof(TDrawBuffer)/sizeof(ushort) больше не является действительным; Такие проверки должны быть удалены.
TApplication теперь предоставляет dosShell() , cascade() и tile() и по умолчанию обрабатывает cmDosShell , cmCascade и cmTile . Эти функции можно настроить, переопределяя getTileRect() и writeShellMsg() . Это то же поведение, что и в версии Pascal.
Поддержка колеса мыши: новое событие мыши evMouseWheel . mwRight колеса указано mwLeft mwDown полевом event.mouse.wheel mwUp
Поддержка кнопки средней мыши: новая кнопка мыши mbMiddleButton .
Поле buttons в событиях evMouseUp больше не пусто. Теперь это указывает, какая кнопка была выпущена.
Поддержка тройного клика: новый флаг событий Mouse meTripleClick .
Методы TRect move , grow , intersect и Union теперь возвращают TRect& вместо того, чтобы быть void , чтобы их можно было приколоть.
TOutlineViewer теперь позволяет корневому узлу иметь братьев и сестер.
Новая функция ushort popupMenu(TPoint where, TMenuItem &aMenu, TGroup *receiver=0) , который порождает TMenuPopup на рабочем столе. См. source/tvision/popupmnu.cpp .
Новый виртуальный метод TMenuItem& TEditor::initContextMenu(TPoint p) , который определяет записи контекстного меню правой кнопкой мыши в TEditor .
fexpand теперь может принимать второй параметр relativeTo .
Новый класс TStringView , вдохновленный std::string_view .
TStringView . TStringView совместим с std::string_view , std::string и const char * (даже nullptr ). Новый класс TSpan<T> , вдохновенный std::span .
Новые классы TDrawSurface и TSurfaceView , см. <tvision/surface.h> .
Подсистемы интеграции системной интеграции ( THardwareInfo , TScreen , TEventQueue ...) теперь инициализируются при построении TApplication впервые, а не до main . Они все еще разрушены на выходе из main .
Новый метод TVMemMgr::reallocateDiscardable() , который можно использовать вдоль allocateDiscardable и freeDiscardable .
New Method TView::textEvent() , который позволяет эффективно получать текст, см. Взаимодействие буфера обмена.
Новый класс TClipboard , см. Взаимодействие буфера обмена.
Поддержка Unicode, см. Unicode.
Поддержка True Color, см. Расширенные цвета.
Новый метод static void TEventQueue::waitForEvents(int timeoutMs) , который может заблокировать до timeoutMs Milliseconds в ожидании событий ввода. Негативные timeoutMs могут быть использованы, чтобы ждать невыполненного времени. Если он блокирует, он имеет побочный эффект обновлений экрана промывки (через TScreen::flushScreen() ). Это вызвано TProgram::getEvent() со static int TProgram::eventTimeoutMs (по умолчанию 20 ) в качестве аргумента, так что цикл событий не превращается в загруженный цикл, потребляющий 100% процессор.
Новый метод static void TEventQueue::wakeUp() , который заставляет цикл события возобновить выполнение, если он заблокирован по адресу TEventQueue::waitForEvents() . Этот метод безопасен для потока, поскольку его цель-разблокировать цикл события из вторичных потоков.
New Method void TView::getEvent(TEvent &, int timeoutMs) , который позволяет ждать события с предоставленным пользователем тайм-аутом (вместо TProgram::eventTimeoutMs ).
Теперь можно указать максимальную ширину текста или максимальное количество символов в TInputLine . Это делается с помощью нового параметра в конструкторе TInputLine , ushort limitMode , который контролирует, как должен рассматриваться второй параметр конструктора, uint limit . Константы ilXXXX определяют возможные значения limitMode :
ilMaxBytes (по умолчанию): текст может быть limit байтами длиной, включая нулевый терминатор.ilMaxWidth : текст может быть ограничен до limit столбцов.ilMaxChars : Текст может содержать limit некомбинирующих символов или графем. Новые функции, которые позволяют получать имена констант Turbo Vision во время выполнения (например, evCommand , kbShiftIns и т. Д.):
void printKeyCode (ostream &, ushort keyCode);
void printControlKeyState (ostream &, ushort controlKeyState);
void printEventCode (ostream &, ushort eventCode);
void printMouseButtonState (ostream &, ushort buttonState);
void printMouseWheelState (ostream &, ushort wheelState);
void printMouseEventFlags (ostream &, ushort eventFlags); Новый класс TKey , который можно использовать для определения новых комбинаций ключей (например, Shift+Alt+Up ), указав код ключа и маску модификаторов ключей:
auto kbShiftAltUp = TKey(kbUp, kbShift | kbAltShift);
assert (kbCtrlA == TKey( ' A ' , kbCtrlShift));
assert (TKey(kbCtrlTab, kbShift) == TKey(kbTab, kbShift | kbCtrlShift));
// Create menu hotkeys.
new TMenuItem( " ~R~estart " , cmRestart, TKey(kbDel, kbCtrlShift | kbAltShift), hcNoContext, " Ctrl-Alt-Del " )
// Examine KeyDown events:
if (event.keyDown == TKey(kbEnter, kbShift))
doStuff ();Новые методы, которые позволяют использовать временные события:
TTimerId TView::setTimer ( uint timeoutMs, int periodMs = - 1 );
void TView::killTimer (TTimerId id); setTimer начинает таймер, который в первый раз выйдет в timeoutMs Milliseconds, а затем в каждом periodMs Milliseconds.
Если periodMs отрицателен, таймер только раз в один раз и автоматически очищается. В противном случае он будет периодически продолжать время, пока killTimer не будет вызовом.
Когда таймер пройдет, событие evBroadcast с командой cmTimerExpired испускается, и message.infoPtr устанавливается на TTimerId истекшего таймера.
События тайм -аута генерируются в TProgram::idle() . Следовательно, они обрабатываются только тогда, когда не доступны события клавиатуры или мыши.
Здесь вы найдете некоторые скриншоты. Не стесняйтесь добавлять свой собственный!
Если вы знаете какие -либо приложения Turbo Vision, исходный код, не был потерян, и это могло бы извлечь выгоду из этого, дайте мне знать.
Если ваше приложение основано на этом проекте, и вы хотели бы, чтобы оно появилось в следующем списке, просто дайте мне знать.
API Turbo Vision была расширена, чтобы позволить приема ввода Unicode и отображение текста Unicode. Поддерживаемая кодировка-UTF-8, по ряду причин:
char * ), поэтому он не требует навязчивых изменений в существующем коде.Обратите внимание, что при построении с Borland C ++ Turbo Vision не поддерживает Unicode. Тем не менее, это не влияет на то, как написаны приложения Turbo Vision, поскольку расширения API предназначены для обеспечения кодирования-агрессии.
Традиционный способ получить текст из события нажатия клавиш заключается в следующем:
// 'ev' is a TEvent, and 'ev.what' equals 'evKeyDown'.
switch (ev.keyDown.keyCode) {
// Key shortcuts are usually checked first.
// ...
default : {
// The character is encoded in the current codepage
// (CP437 by default).
char c = ev. keyDown . charScan . charCode ;
// ...
}
} Некоторые из существующих классов Turbo Vision, которые касаются ввода текста, все еще зависят от этой методологии, которая не изменилась. Однобайтные символы, будучи репрезентативными в текущем коде, продолжают доступны в ev.keyDown.charScan.charCode .
Поддержка Unicode состоит в двух новых полях в ev.keyDown (который является struct KeyDownEvent ):
char text[4] , который может содержать все, что было прочитано с терминала: обычно последовательность UTF-8, но, возможно, любые необработанные данные.uchar textLength , которая представляет собой количество байтов данных, доступных в text , от 0 до 4. Обратите внимание, что text строка не является нулевой. Вы можете получить TStringView из метода KeyDownEvent getText() .
Таким образом, символ Unicode может быть извлечен TEvent .
switch (ev.keyDown.keyCode) {
// ...
default : {
std::string_view sv = ev. keyDown . getText ();
processText (sv);
}
} Давайте посмотрим на это с другой точки зрения. Если пользователь типирует ñ , сгенерируется TEvent keyDown .
KeyDownEvent {
union {
. keyCode = 0xA4 ,
. charScan = CharScanType {
. charCode = 164 ( ' ñ ' ), // In CP437
. scanCode = 0
}
},
. controlKeyState = 0x200 (kbInsState),
. text = { ' xC3 ' , ' xB1 ' }, // In UTF-8
. textLength = 2
} Однако, если они введите € произойдет следующее:
KeyDownEvent {
union {
. keyCode = 0x0 (kbNoKey), // '€' not part of CP437
. charScan = CharScanType {
. charCode = 0 ,
. scanCode = 0
}
},
. controlKeyState = 0x200 (kbInsState),
. text = { ' xE2 ' , ' x82 ' , ' xAC ' }, // In UTF-8
. textLength = 3
} Если вместо этого нажат ярлык клавиши, text пуст:
KeyDownEvent {
union {
. keyCode = 0xB (kbCtrlK),
. charScan = CharScanType {
. charCode = 11 ( ' ♂ ' ),
. scanCode = 0
}
},
. controlKeyState = 0x20C (kbCtrlShift | kbInsState),
. text = {},
. textLength = 0
}Итак, короче говоря: представления, разработанные без ввода Unicode, будут продолжать работать точно так же, как и раньше, и взгляды, которые хотят быть Unicode-Aware, не будут иметь проблем с тем, чтобы быть так.
В оригинальном дизайне Turbo Vision используется 16 битов для представления ячейки экрана - 8 -бил для персонажа и 8 бит для атрибутов Bios Color.
Новый тип TScreenCell определяется в <tvision/scrncell.h> , который способен удерживать ограниченное количество Codepoints UTF-8 в дополнение к расширенным атрибутам (жирный, подчеркнутый, курсив ...). Тем не менее, вы не должны записывать текст в TScreenCell напрямую, но вместо этого использовать функции API-плата Unicode-Aware.
Персонаж, предоставленный в качестве аргумента для любой из функций API Turbo Vision, которые касаются отображения текста, интерпретируется следующим образом:
0x00 до 0xFF интерпретируются как символы в активной коде. Например, 0x7F отображается как ⌂ и 0xF0 как ≡ если использует CP437. В качестве исключения 0x00 всегда отображается как обычное пространство. Все эти символы имеют ширину в одну колонну. Например, строка "╔[xFE]╗" может отображаться как ╔[■]╗ . Это означает, что символы для рисования коробок могут быть смешаны с UTF-8 в целом, что полезно для обратной совместимости. Однако, если вы полагаетесь на это поведение, вы можете получить неожиданные результаты: например, "xC4xBF" -это действительная последовательность UTF-8 и отображается как Ŀ вместо ─┐ .
Одной из проблем поддержки Unicode является существование символов с двойной шириной и сочетание символов. Это противоречит первоначальному предположению Turbo Vision, что экран представляет собой сетку клеток, занятых одним символом. Тем не менее, эти случаи обрабатываются следующим образом:
Персонажи с двойной шириной можно нарисовать в любом месте на экране, и ничего плохого не произойдет, если они частично перекрываются с другими символами.
Персонажи с нулевой шириной накладывают предыдущий персонаж. Например, последовательность में состоит из символа с одной шириной म и комбинирующих символов े и ं . В этом случае три кодепояна Unicode вписываются в одну и ту же ячейку.
ZERO WIDTH JOINER ( U+200D ) всегда пропускается, поскольку он слишком усложняет ситуацию. Например, это может повернуть строку, как "??" (4 колонны шириной) в "??" (2 колонны шириной). Не все терминальные эмуляторы уважают ZWJ, поэтому, чтобы получить предсказуемые результаты, Turbo Vision печатает оба "??" и "??" как ?? Полем
Никакие заметные графические глюки не произойдут до тех пор, пока ваш эмулятор терминала уважает ширину символов, измеренную с помощью wcwidth .
Вот пример таких символов в Turbo Text Editor: 
Обычный способ написания на экране - использование TDrawBuffer . Было добавлено несколько методов, и другие изменили свое значение:
void TDrawBuffer::moveChar ( ushort indent, char c, TColorAttr attr, ushort count);
void TDrawBuffer::putChar ( ushort indent, char c); c всегда интерпретируется как персонаж в активной коде.
ushort TDrawBuffer::moveStr ( ushort indent, TStringView str, TColorAttr attr);
ushort TDrawBuffer::moveCStr ( ushort indent, TStringView str, TAttrPair attrs); str интерпретируется в соответствии с правилами, раскрытыми ранее.
ushort TDrawBuffer::moveStr ( ushort indent, TStringView str, TColorAttr attr, ushort maxWidth, ushort strOffset = 0 ); // New
ushort TDrawBuffer::moveCStr ( ushort indent, TStringView str, TColorAttr attr, ushort maxWidth, ushort strOffset = 0 ); // New str интерпретируется в соответствии с правилами, раскрытыми ранее, но:
maxWidth указывает максимальное количество текста, которое следует копировать из str , измеряемого по ширине текста (не в байтах).strOffset указывает начальную позицию в str , откуда копировать, измеряемое по ширине текста (не в байтах). Это полезно для горизонтальной прокрутки. Если strOffset указывает на середину персонажа с двойной шириной, вместо правой половины персонажа с двойной шириной будет скопировано пространство, поскольку невозможно сделать такую вещь.Возвратные значения - это количество ячеек в буфере, которые были фактически заполнены текстом (что такое же, как ширина копируемого текста).
void TDrawBuffer::moveBuf ( ushort indent, const void *source, TColorAttr attr, ushort count); Название этой функции вводит в заблуждение. Даже в своей первоначальной реализации source рассматривается как строка. Таким образом, это эквивалентно moveStr(indent, TStringView((const char*) source, count), attr) .
Есть и другие полезные функции с юникодом:
int cstrlen (TStringView s); Возвращает отображаемую длину s в соответствии с вышеупомянутыми правилами, отбрасывая ~ символы.
int strwidth (TStringView s); // New Возвращает отображаемую длину s .
На Borland C ++ эти методы предполагают однобайт-кодирование, а все символы шириной в один столб. Это позволяет писать методы кодирования-алкотического draw() и handleEvent() , которые работают на обеих платформах без единого #ifdef .
Приведенные выше функции реализованы с использованием функций из пространства TText имен, другого расширения API. Вам придется использовать их напрямую, если вы хотите заполнить объекты TScreenCell текстом вручную. Чтобы привести пример, ниже приведены некоторые из функций TText . Вы можете найти все из них с полными описаниями в <tvision/ttext.h> .
size_t TText::next (TStringView text);
size_t TText::prev (TStringView text, size_t index);
void TText::drawChar (TSpan<TScreenCell> cells, char c);
size_t TText::drawStr (TSpan<TScreenCell> cells, size_t indent, TStringView text, int textIndent);
bool TText::drawOne (TSpan<TScreenCell> cells, size_t &i, TStringView text, size_t &j); Для привлечения буферов TScreenCell в представление доступны следующие методы:
void TView::writeBuf ( short x, short y, short w, short h, const TScreenCell *b); // New
void TView::writeLine ( short x, short y, short w, short h, const TScreenCell *b); // New Это настолько просто, насколько это возможно. Давайте изменить hello.cpp следующим образом:
TMenuBar * THelloApp::initMenuBar ( TRect r )
{
r. b . y = r. a . y + 1 ;
return new TMenuBar ( r,
* new TSubMenu ( " ~Ñ~ello " , kbAltH ) +
* new TMenuItem ( "階~毎~料入報最... " , GreetThemCmd, kbAltG ) +
* new TMenuItem ( "五劫~の~擦り切れ" , cmYes, kbNoKey, hcNoContext ) +
* new TMenuItem ( " העברית ~א~ינטרנט " , cmNo, kbNoKey, hcNoContext ) +
newLine () +
* new TMenuItem ( " E~x~it " , cmQuit, cmQuit, hcNoContext, " Alt-X " )
);
}
TStatusLine * THelloApp::initStatusLine ( TRect r )
{
r. a . y = r. b . y - 1 ;
return new TStatusLine ( r,
* new TStatusDef ( 0 , 0xFFFF ) +
* new TStatusItem ( " ~Alt-Ç~ Exit " , kbAltX, cmQuit ) +
* new TStatusItem ( 0 , kbF10, cmMenu )
);
}Вот как это выглядит:

draw() Ниже приведен выдержка из старой реализации TFileViewer::draw() (часть приложения tvdemo ), которая не рисует текст Unicode должным образом:
if (delta.y + i < fileLines-> getCount ()) {
char s[maxLineLength+ 1 ];
p = ( char *)(fileLines-> at (delta. y +i));
if (p == 0 || strlen (p) < delta. x )
s[ 0 ] = EOS;
else
strnzcpy (s, p+delta. x , maxLineLength+ 1 );
b. moveStr ( 0 , s, c);
}
writeBuf ( 0 , i, size.x, 1 , b ); Все, что он делает, это перемещает часть строки в fileLines в b , который является TDrawBuffer . delta - это TPoint , представляющая смещение прокрутки в текстовом представлении, и i является индексом обработанной видимой линии. c - цвет текста. Несколько проблем присутствуют:
TDrawBuffer::moveStr(ushort, const char *, TColorAttr) принимает строку с нулевым концевым. Чтобы пройти подстроение текущей строки, копия вносится в s , рискуя переполнением буфера. Случай, когда линия не вписывается в s не обрабатывается, поэтому в большинстве случаев будет maxLineLenght символы. Более того, мультибитный персонаж, близкий к положению maxLineLength можно скопировать не полностью и отображаться в виде мусора.delta.x - первый видимый столбец. Благодаря мультибит-кодируемому тексту больше не правда, что такой столбец начинается в delta.x .Ниже приведена исправленная версия кода выше, которая правильно обрабатывает Unicode:
if (delta.y + i < fileLines-> getCount ()) {
p = ( char *)(fileLines-> at (delta. y +i));
if (p)
b. moveStr ( 0 , p, c, size. x , delta. x );
}
writeBuf ( 0 , i, size.x, 1 , b ); Перегрузка moveStr используемой здесь, - это TDrawBuffer::moveStr(ushort indent, TStringView str, TColorAttr attr, ushort width, ushort begin) . Эта функция не только обеспечивает поддержку Unicode, но и помогает нам написать более чистый код и преодолеть некоторые из предыдущих ранее ограничений:
maxLineLength .moveStr заботится о печати строку, начиная с колонны delta.x . Нам даже не нужно беспокоиться о том, сколько байтов соответствует столбцам delta.x .moveStr is instructed to copy at most size.x columns of text without us having to care about how many bytes that is nor dealing with edge cases. The code is written in an encoding-agnostic way and will work whether multibyte characters are being considered or not.TView::writeBuf already takes care of not writing beyond it. Yet it is interesting to see how an unnecessary step not only was limiting functionality but also was prone to bugs. Support for creating Unicode-aware views is in place, and most views in the original Turbo Vision library have been adapted to handle Unicode.
The following views can display Unicode text properly. Some of them also do horizontal scrolling or word wrapping; all of that should work fine.
TStaticText ( 7b15d45d ). TFrame ( 81066ee5 ). TStatusLine ( 477b3ae9 ). THistoryViewer ( 81066ee5 ). THelpViewer ( 81066ee5 , 8c7dac2a , 20f331e3 ). TListViewer ( 81066ee5 ). TMenuBox ( 81066ee5 ). TTerminal ( ee821b69 ). TOutlineViewer ( 6cc8cd38 ). TFileViewer (from the tvdemo application) ( 068bbf7a ). TFilePane (from the tvdir application) ( 9bcd897c ).The following views can, in addition, process Unicode text or user input:
TInputLine ( 81066ee5 , cb489d42 ). TEditor ( 702114dc ). Instances are in UTF-8 mode by default. You may switch back to single-byte mode by pressing Ctrl+P . This only changes how the document is displayed and the encoding of user input; it does not alter the document. This class is used in the tvedit application; you may test it there.Views not in this list may not have needed any corrections or I simply forgot to fix them. Please submit an issue if you notice anything not working as expected.
Use cases where Unicode is not supported (not an exhaustive list):
TMenuBox , TStatusLine , TButton ...). Originally, Turbo Vision offered no integration with the system clipboard, since there was no such thing on MS-DOS.
It did offer the possibility of using an instance of TEditor as an internal clipboard, via the TEditor::clipboard static member. However, TEditor was the only class able to interact with this clipboard. It was not possible to use it with TInputLine , for example.
Turbo Vision applications are now most likely to be ran in a graphical environment through a terminal emulator. In this context, it would be desirable to interact with the system clipboard in the same way as a regular GUI application would do.
To deal with this, a new class TClipboard has been added which allows accessing the system clipboard. If the system clipboard is not accessible, it will instead use an internal clipboard.
On Windows (including WSL) and macOS, clipboard integration is supported out-of-the-box.
On Unix systems other than macOS, it is necessary to install some external dependencies. See runtime requirements.
For applications running remotely (eg through SSH), clipboard integration is supported in the following situations:
ssh -X ).allowWindowOps option is enabled. Additionally, it is always possible to paste text using your terminal emulator's own Paste command (usually Ctrl+Shift+V or Cmd+V ).
To use the TClipboard class, define the macro Uses_TClipboard before including <tvision/tv.h> .
static void TClipboard::setText (TStringView text); Sets the contents of the system clipboard to text . If the system clipboard is not accessible, an internal clipboard is used instead.
static void TClipboard::requestText (); Requests the contents of the system clipboard asynchronously, which will be later received in the form of regular evKeyDown events. If the system clipboard is not accessible, an internal clipboard is used instead.
A Turbo Vision application may receive a Paste event for two different reasons:
TClipboard::requestText() was invoked. In both cases the application will receive the clipboard contents in the form of regular evKeyDown events. These events will have a kbPaste flag in keyDown.controlKeyState so that they can be distinguished from regular key presses.
Therefore, if your view can handle user input it will also handle Paste events by default. However, if the user pastes 5000 characters, the application will behave as if the user pressed the keyboard 5000 times. This involves drawing views, completing the event loop, updating the screen..., which is far from optimal if your view is a text editing component, for example.
For the purpose of dealing with this situation, another function has been added:
bool TView::textEvent (TEvent &event, TSpan< char > dest, size_t &length); textEvent() attempts to read text from consecutive evKeyDown events and stores it in a user-provided buffer dest . It returns false when no more events are available or if a non-text event is found, in which case this event is saved with putEvent() so that it can be processed in the next iteration of the event loop. Finally, it calls clearEvent(event) .
The exact number of bytes read is stored in the output parameter length , which will never be larger than dest.size() .
Here is an example on how to use it:
// 'ev' is a TEvent, and 'ev.what' equals 'evKeyDown'.
// If we received text from the clipboard...
if (ev.keyDown.controlKeyState & kbPaste) {
char buf[ 512 ];
size_t length;
// Fill 'buf' with the text in 'ev' and in
// upcoming events from the input queue.
while ( textEvent (ev, buf, length)) {
// Process 'length' bytes of text in 'buf'...
}
} The standard views TEditor and TInputLine react to the cmCut , cmCopy and cmPaste commands. However, your application first has to be set up to use these commands. Например:
TStatusLine * TMyApplication::initStatusLine ( TRect r )
{
r. a . y = r. b . y - 1 ;
return new TStatusLine ( r,
* new TStatusDef ( 0 , 0xFFFF ) +
// ...
* new TStatusItem ( 0 , kbCtrlX, cmCut ) +
* new TStatusItem ( 0 , kbCtrlC, cmCopy ) +
* new TStatusItem ( 0 , kbCtrlV, cmPaste ) +
// ...
);
} TEditor and TInputLine automatically enable and disable these commands. For example, if a TEditor or TInputLine is focused, the cmPaste command will be enabled. If there is selected text, the cmCut and cmCopy commands will also be enabled. If no TEditor or TInputLine s are focused, then these commands will be disabled.
The Turbo Vision API has been extended to allow more than the original 16 colors.
Colors can be specified using any of the following formats:
xterm-256color palette indices (8-bit).Although Turbo Vision applications are likely to be ran in a terminal emulator, the API makes no assumptions about the display device. That is to say, the complexity of dealing with terminal emulators is hidden from the programmer and managed by Turbo Vision itself.
For example: color support varies among terminals. If the programmer uses a color format not supported by the terminal emulator, Turbo Vision will quantize it to what the terminal can display. The following images represent the quantization of a 24-bit RGB picture to 256, 16 and 8 color palettes:
| 24-bit color (original) | 256 colors |
|---|---|
![]() | ![]() |
| 16 colors | 8 colors (bold as bright) |
|---|---|
![]() | ![]() |
Extended color support basically comes down to the following:
uchar . ushort is used to represent attribute pairs. This is still the case when using Borland C++.TColorAttr has been added which replaces uchar . It specifies a foreground and background color and a style. Colors can be specified in different formats (BIOS color attributes, 24-bit RGB...). Styles are the typical ones (bold, italic, underline...). There's also TAttrPair , which replaces ushort .TDrawBuffer 's methods, which used to take uchar or ushort parameters to specify color attributes, now take TColorAttr or TAttrPair .TPalette , which used to contain an array of uchar , now contains an array of TColorAttr . The TView::mapColor method also returns TColorAttr instead of uchar .TView::mapColor has been made virtual so that the palette system can be bypassed without having to rewrite any draw methods.TColorAttr and TAttrPair can be initialized with and casted into uchar and ushort in a way such that legacy code still compiles out-of-the-box without any change in functionality.Below is a more detailed explanation aimed at developers.
In the first place we will explain the data types the programmer needs to know in order to take advantage of the extended color support. To get access to them, you may have to define the macro Uses_TColorAttr before including <tvision/tv.h> .
All the types described in this section are trivial . This means that they can be memset 'd and memcpy 'd. But variables of these types are uninitialized when declared without initializer, just like primitive types. So make sure you don't manipulate them before initializing them.
Several types are defined which represent different color formats. The reason why these types exist is to allow distinguishing color formats using the type system. Some of them also have public fields which make it easier to manipulate individual bits.
TColorBIOS represents a BIOS color. It allows accessing the r , g , b and bright bits individually, and can be casted implicitly into/from uint8_t .
The memory layout is:
b ).g ).r ).bright ). Examples of TColorBIOS usage:
TColorBIOS bios = 0x4 ; // 0x4: red.
bios.bright = 1 ; // 0xC: light red.
bios.b = bios.r; // 0xD: light magenta.
bios = bios ^ 3 ; // 0xE: yellow.
uint8_t c = bios; // Implicit conversion to integer types.In terminal emulators, BIOS colors are mapped to the basic 16 ANSI colors.
TColorRGB represents a color in 24-bit RGB. It allows accessing the r , g and b bit fields individually, and can be casted implicitly into/from uint32_t .
The memory layout is:
b ).g ).r ). Examples of TColorRGB usage:
TColorRGB rgb = 0x9370DB ; // 0xRRGGBB.
rgb = { 0x93 , 0x70 , 0xDB }; // {R, G, B}.
rgb = rgb ^ 0xFFFFFF ; // Negated.
rgb.g = rgb.r & 0x88 ; // Access to individual components.
uint32_t c = rgb; // Implicit conversion to integer types. TColorXTerm represents an index into the xterm-256color color palette. It can be casted into and from uint8_t .
TColorDesired TColorDesired represents a color which the programmer intends to show on screen, encoded in any of the supported color types.
A TColorDesired can be initialized in the following ways:
As a BIOS color: with a char literal or a TColorBIOS object:
TColorDesired bios1 = ' xF ' ;
TColorDesired bios2 = TColorBIOS( 0xF ); As a RGB color: with an int literal or a TColorRGB object:
TColorDesired rgb1 = 0xFF7700 ; // 0xRRGGBB.
TColorDesired rgb2 = TColorRGB( 0xFF , 0x77 , 0x00 ); // {R, G, B}.
TColorDesired rgb3 = TColorRGB( 0xFF7700 ); // 0xRRGGBB. As an XTerm palette index: with a TColorXTerm object.
As the terminal default color: through zero-initialization:
TColorDesired def1 {};
// Or with 'memset':
TColorDesired def2;
memset (&def2, 0 , sizeof (def2)); TColorDesired has methods to query the contained color, but you will usually not need to use them. See the struct definition in <tvision/colors.h> for more information.
Trivia: the name is inspired by Scintilla's ColourDesired .
TColorAttr TColorAttr describes the color attributes of a screen cell. This is the type you are most likely to interact with if you intend to change the colors in a view.
A TColorAttr is composed of:
A foreground color, of type TColorDesired .
A background color, of type TColorDesired .
A style bitmask containing a combination of the following flags:
slBold .slItalic .slUnderline .slBlink .slReverse .slStrike . These flags are based on the basic display attributes selectable through ANSI escape codes. The results may vary between terminal emulators. slReverse is probably the least reliable of them: prefer using the TColorAttr reverseAttribute(TColorAttr attr) free function over setting this flag.
The most straight-forward way to create a TColorAttr is by means of the TColorAttr(TColorDesired fg, TColorDesired bg, ushort style=0) and TColorAttr(int bios) constructors:
// Foreground: RGB 0x892312
// Background: RGB 0x7F00BB
// Style: Normal.
TColorAttr a1 = { TColorRGB ( 0x89 , 0x23 , 0x12 ), TColorRGB ( 0x7F , 0x00 , 0xBB )};
// Foreground: BIOS 0x7.
// Background: RGB 0x7F00BB.
// Style: Bold, Italic.
TColorAttr a2 = { ' x7 ' , 0x7F00BB , slBold | slItalic};
// Foreground: Terminal default.
// Background: BIOS 0xF.
// Style: Normal.
TColorAttr a3 = {{}, TColorBIOS ( 0xF )};
// Foreground: Terminal default.
// Background: Terminal default.
// Style: Normal.
TColorAttr a4 = {};
// Foreground: BIOS 0x0
// Background: BIOS 0x7
// Style: Normal
TColorAttr a5 = 0x70 ; The fields of a TColorAttr can be accessed with the following free functions:
TColorDesired getFore ( const TColorAttr &attr);
TColorDesired getBack ( const TColorAttr &attr);
ushort getStyle ( const TColorAttr &attr);
void setFore (TColorAttr &attr, TColorDesired fg);
void setBack (TColorAttr &attr, TColorDesired bg);
void setStyle (TColorAttr &attr, ushort style);TAttrPair TAttrPair is a pair of TColorAttr , used by some API functions to pass two attributes at once.
You may initialize a TAttrPair with the TAttrPair(const TColorAttrs &lo, const TColorAttrs &hi) constructor:
TColorAttr cNormal = { 0x234983 , 0x267232 };
TColorAttr cHigh = { 0x309283 , 0x127844 };
TAttrPair attrs = {cNormal, cHigh};
TDrawBuffer b;
b.moveCStr( 0 , " Normal text, ~Highlighted text~ " , attrs); The attributes can be accessed with the [0] and [1] subindices:
TColorAttr lo = { 0x892343 , 0x271274 };
TColorAttr hi = ' x93 ' ;
TAttrPair attrs = {lo, hi};
assert (lo == attrs[ 0 ]);
assert (hi == attrs[ 1 ]);TView Views are commonly drawn by means of a TDrawBuffer . Most TDrawBuffer member functions take color attributes by parameter. Например:
ushort TDrawBuffer::moveStr ( ushort indent, TStringView str, TColorAttr attr);
ushort TDrawBuffer::moveCStr ( ushort indent, TStringView str, TAttrPair attrs);
void TDrawBuffer::putAttribute ( ushort indent, TColorAttr attr);However, the views provided with Turbo Vision usually store their color information in palettes. A view's palette can be queried with the following member functions:
TColorAttr TView::mapColor (uchar index);
TAttrPair TView::getColor ( ushort indices); mapColor looks up a single color attribute in the view's palette, given an index into the palette. Remember that the palette indices for each view class can be found in the Turbo Vision headers. For example, <tvision/views.h> says the following about TScrollBar :
/* ---------------------------------------------------------------------- */
/* class TScrollBar */
/* */
/* Palette layout */
/* 1 = Page areas */
/* 2 = Arrows */
/* 3 = Indicator */
/* ---------------------------------------------------------------------- */ getColor is a helper function that allows querying two cell attributes at once. Each byte in the indices parameter contains an index into the palette. The TAttrPair result contains the two cell attributes.
For example, the following can be found in the draw method of TMenuBar :
TAttrPair cNormal = getColor( 0x0301 );
TAttrPair cSelect = getColor( 0x0604 );Which would be equivalent to this:
TAttrPair cNormal = { mapColor ( 1 ), mapColor ( 3 )};
TAttrPair cSelect = { mapColor ( 4 ), mapColor ( 6 )}; As an API extension, the mapColor method has been made virtual . This makes it possible to override Turbo Vision's hierarchical palette system with a custom solution without having to rewrite the draw() method.
So, in general, there are three ways to use extended colors in views:
mapColor method: // The 'TMyScrollBar' class inherits from 'TScrollBar' and overrides 'TView::mapColor'.
TColorAttr TMyScrollBar::mapColor (uchar index) noexcept
{
// In this example the values are hardcoded,
// but they could be stored elsewhere if desired.
switch ( index )
{
case 1 : return { 0x492983 , 0x826124 }; // Page areas.
case 2 : return { 0x438939 , 0x091297 }; // Arrows.
case 3 : return { 0x123783 , 0x329812 }; // Indicator.
default : return errorAttr;
}
} By providing extended color attributes directly to TDrawBuffer methods, if the palette system is not being used. Например:
// The 'TMyView' class inherits from 'TView' and overrides 'TView::draw'.
void TMyView::draw ()
{
TDrawBuffer b;
TColorAttr color { 0x1F1C1B , 0xFAFAFA , slBold};
b. moveStr ( 0 , " This is bold black text over a white background " , color);
/* ... */
}By modifying the palettes. There are two ways to do this:
TColorAttr . Например: void updateAppPalette ()
{
TPalette &pal = TProgram::application-> getPalete ();
pal[ 1 ] = { 0x762892 , 0x828712 }; // TBackground.
pal[ 2 ] = { 0x874832 , 0x249838 , slBold}; // TMenuView normal text.
pal[ 3 ] = {{}, {}, slItalic | slUnderline}; // TMenuView disabled text.
/* ... */
} static const TColorAttr cpMyApp[] =
{
{ 0x762892 , 0x828712 }, // TBackground.
{ 0x874832 , 0x249838 , slBold}, // TMenuView normal text.
{{}, {}, slItalic | slUnderline}, // TMenuView disabled text.
/* ... */
};
// The 'TMyApp' class inherits from 'TApplication' and overrides 'TView::getPalette'.
TPalette & TMyApp::getPalette () const
{
static TPalette palette (cpMyApp);
return palette;
} TScreen::screenMode exposes some information about the display's color support:
(TScreen::screenMode & 0xFF) == TDisplay::smMono , the display is monocolor (only relevant in DOS).(TScreen::screenMode & 0xFF) == TDisplay::smBW80 , the display is grayscale (only relevant in DOS).(TScreen::screenMode & 0xFF) == TDisplay::smCO80 , the display supports at least 16 colors.TScreen::screenMode & TDisplay::smColor256 , the display supports at least 256 colors.TScreen::screenMode & TDisplay::smColorHigh , the display supports even more colors (eg 24-bit color). TDisplay::smColor256 is also set in this case. The types defined previously represent concepts that are also important when developing for Borland C++:
| Концепция | Layout in Borland C++ | Layout in modern platforms |
|---|---|---|
| Color Attribute | uchar . A BIOS color attribute. | struct TColorAttr . |
| Цвет | A 4-bit number. | struct TColorDesired . |
| Attribute Pair | ushort . An attribute in each byte. | struct TAttrPair . |
One of this project's key principles is that the API should be used in the same way both in Borland C++ and modern platforms, that is to say, without the need for #ifdef s. Another principle is that legacy code should compile out-of-the-box, and adapting it to the new features should increase complexity as little as possible.
Backward-compatibility is accomplished in the following way:
In Borland C++, TColorAttr and TAttrPair are typedef 'd to uchar and ushort , respectively.
In modern platforms, TColorAttr and TAttrPair can be used in place of uchar and ushort , respectively, since they are able to hold any value that fits into them and can be casted implicitly into/from them.
A TColorAttr initialized with uchar represents a BIOS color attribute. When converting back to uchar , the following happens:
fg and bg are BIOS colors, and style is cleared, the resulting uchar represents the same BIOS color attribute contained in the TColorAttr (as in the code above).uchar / ushort with TColorAttr / TAttrPair if they intend to support the extended color attributes. The same goes for TAttrPair and ushort , considering that it is composed of two TColorAttr .
A use case of backward-compatibility within Turbo Vision itself is the TPalette class, core of the palette system. In its original design, it used a single data type ( uchar ) to represent different things: array length, palette indices or color attributes.
The new design simply replaces uchar with TColorAttr . This means there are no changes in the way TPalette is used, yet TPalette is now able to store extended color attributes.
TColorDialog hasn't been remodeled, and thus it can't be used to pick extended color attributes at runtime.
The following pattern of code is common across draw methods of views:
void TMyView::draw ()
{
ushort cFrame, cTitle;
if (state & sfDragging)
{
cFrame = 0x0505 ;
cTitle = 0x0005 ;
}
else
{
cFrame = 0x0503 ;
cTitle = 0x0004 ;
}
cFrame = getColor (cFrame);
cTitle = getColor (cTitle);
/* ... */
} In this case, ushort is used both as a pair of palette indices and as a pair of color attributes. getColor now returns a TAttrPair , so even though this compiles out-of-the-box, extended attributes will be lost in the implicit conversion to ushort .
The code above still works just like it did originally. It's only non-BIOS color attributes that don't produce the expected result. Because of the compatibility between TAttrPair and ushort , the following is enough to enable support for extended color attributes:
- ushort cFrame, cTitle;
+ TAttrPair cFrame, cTitle;Nothing prevents you from using different variables for palette indices and color attributes, which is what should actually be done. The point of backward-compatibility is the ability to support new features without changing the program's logic, that is to say, minimizing the risk of increasing code complexity or introducing bugs.