Uma porta moderna do Turbo Vision 2.0, a estrutura clássica para interfaces de usuário baseadas em texto. Agora plataforma cruzada e com suporte Unicode.

Comecei isso como um projeto pessoal no final de 2018. Em maio de 2020, considerei que estava muito perto de paridade com o original e decidi torná -lo aberto.
Os objetivos originais deste projeto foram:
A certa altura, considerei o suficiente e que qualquer tentativa de renovar a biblioteca e superar suas limitações originais exigiria estender a API ou quebrar a compatibilidade com versões anteriores, e que uma grande reescrita seria provavelmente necessária.
No entanto, entre julho e agosto de 2020, encontrei o caminho para integrar o suporte Unicode de pleno direito à arquitetura existente, escrevi o Turbo Text Editor e também disponibilizei os novos recursos disponíveis no Windows. Portanto, estou confiante de que o Turbo Vision agora pode atender a muitas das expectativas de usuários e programadores modernos.
A localização original deste projeto é https://github.com/magiblot/tvision.
Muita coisa mudou desde que Borland criou o Turbo Vision no início dos anos 90. Hoje, muitas ferramentas da GUI separam a especificação de aparência da especificação de comportamento, usam linguagens mais seguras ou dinâmicas que não se encaixam no erro e suportam programação paralela ou assíncrona, ou ambas.
O Turbo Vision não se destaca em nenhum deles, mas certamente supera muitos dos problemas que os programadores ainda enfrentam hoje ao escrever aplicativos de terminais:
Esqueça os recursos do terminal e a E/S direta do terminal. Ao escrever um aplicativo Turbo Vision, tudo o que você precisa se importar é com o que deseja que seu aplicativo se comporte e pareça - não há necessidade de adicionar soluções alternativas ao seu código. A Turbo Vision se esforça melhor para produzir os mesmos resultados em todos os ambientes. Por exemplo: para obter uma cor de fundo brilhante no console Linux, o atributo Blink deve ser definido. Turbo Vision faz isso por você.
Reutilize o que já foi feito. O Turbo Vision fornece muitas classes de widgets (também conhecidas como vistas ), incluindo janelas redigíveis e sobrepostas, menus de retirada, caixas de diálogo, botões, barras de rolagem, caixas de entrada, caixas de seleção e botões de rádio. Você pode usá -los e estendê -los; Mas, mesmo se você preferir criar o seu próprio, o Turbo Vision já lida com a expedição de eventos, exibição de caracteres unicode de largura de largura, etc.: Você não precisa perder tempo reescrevendo nada disso.
Você pode imaginar escrever uma interface baseada em texto que funcione no Linux e no Windows (e, portanto, é uma plataforma cruzada) pronta para uso, sem #ifdef s? Turbo Vision torna isso possível. Primeiro: o Turbo Vision continua usando as matrizes char em vez de confiar no wchar_t ou TCHAR definido e dependente da plataforma e dependente da plataforma. Segundo: graças ao suporte UTF-8 em setlocale nas versões recentes do RTL da Microsoft, o código como o seguinte funcionará como pretendido:
std::ifstream f ( "コンピュータ.txt " ); // On Windows, the RTL converts this to the system encoding on-the-fly. Você pode começar o Guia do Usuário do Turbo Vision for C ++ e observar os aplicativos de amostra hello , tvdemo e tvedit . Depois de entender o básico, sugiro que você dê uma olhada no Turbo Vision 2.0 Programming Guide, que é, na minha opinião, mais intuitivo e mais fácil de entender, apesar de usar Pascal. Até então, você provavelmente estará interessado no exemplo palette , que contém uma descrição detalhada de como as paletas são usadas.
Não se esqueça de conferir os recursos e as seções de alterações da API também.
Este projeto não possui lançamentos estáveis por enquanto. Se você é um desenvolvedor, tente seguir o comprometimento mais recente e relatar todos os problemas que encontrar ao atualizar.
Se você deseja apenas testar os aplicativos de demonstração:
examples-dos32.zip : executáveis de 32 bits construídos com Borland C ++. Sem suporte Unicode.examples-x86.zip : executáveis de 32 bits criados com MSVC. Windows Vista ou posteriormente necessário.examples-x64.zip : executáveis de 64 bits criados com MSVC. X64 Windows Vista ou posteriormente necessário. O Turbo Vision pode ser construído como uma biblioteca estática com CMake e GCC/CLANG.
cmake . -B ./build -DCMAKE_BUILD_TYPE=Release && # Could also be 'Debug', 'MinSizeRel' or 'RelWithDebInfo'.
cmake --build ./build # or `cd ./build; make` As versões CMake com mais de 3,13 podem não suportar a opção -B . Você pode tentar o seguinte: em vez disso:
mkdir -p build ; cd build
cmake .. -DCMAKE_BUILD_TYPE=Release &&
cmake --build .O acima produz os seguintes arquivos:
libtvision.a , que é a Biblioteca Turbo Vision.hello , tvdemo , tvedit , tvdir , que foram incluídas com a visão turbo original (embora alguns deles tenham algumas melhorias).mmenu e palette do suporte técnico de Borland.tvhc , o Turbo Vision Ajuda o compilador. A biblioteca e os executáveis podem ser encontrados em ./build .
Os requisitos de construção são:
libncursesw (observe o 'w').libgpm para suporte ao mouse no console Linux (opcional). Se sua distribuição fornecer pacotes de desenvolvimento separados (por exemplo, libncurses-dev , libgpm-dev em distos baseados em Debian), instale-os também.
Os requisitos de tempo de execução são:
xsel ou xclip para suporte à área de transferência em ambientes X11.wl-clipboard para suporte à área de transferência em ambientes Wayland. A linha de comando mínima necessária para criar um aplicativo Turbo Vision (por exemplo, hello.cpp com o GCC) a partir da raiz deste projeto é:
g++ -std=c++14 -o hello hello.cpp ./build/libtvision.a -Iinclude -lncursesw -lgpmVocê também pode precisar:
-Iinclude/tvision Se o seu aplicativo usar o Turbo Vision 1.x inclui ( #include <tv.h> em vez de #include <tvision/tv.h> ).
-Iinclude/tvision/compat/borland Se o seu aplicativo incluir cabeçalhos de Borland ( dir.h , iostream.h , etc.).
No gentoo (e possivelmente outros): -ltinfow se tanto libtinfo.so quanto libtinfow.so estiverem disponíveis em seu sistema. Caso contrário, você poderá obter uma falha de segmentação ao executar aplicativos Turbo Vision (#11). Observe que tinfo é incluído com ncurses .
-lgpm só é necessário se o Turbo Vision foi construído com o suporte libgpm .
Os cabeçalhos de compatibilidade com versões anteriores include/tvision/compat/borland imitam o Borland C ++ RTL. O código -fonte da Turbo Vision ainda depende deles e eles podem ser úteis se portar aplicativos antigos. Isso também significa que a inclusão de tvision/tv.h trará vários nomes std para o espaço de nome global.
O processo de construção com o MSVC é um pouco mais complexo, pois há mais opções para escolher. Observe que você precisará de diretórios de construção diferentes para diferentes arquiteturas de destino. Por exemplo, para gerar binários otimizados:
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'. No exemplo acima, tvision.lib e os aplicativos de exemplo serão colocados em ./build/Release .
Se você deseja vincular o Turbo Vision estaticamente à biblioteca de tempo de execução da Microsoft ( /MT em vez de /MD ), ative a opção TV_USE_STATIC_RTL ( -DTV_USE_STATIC_RTL=ON ao chamar cmake ).
Se você deseja vincular um aplicativo contra o Turbo Vision, observe que o MSVC não permitirá que você misture /MT com /MD ou Debug com binários que não são de debug. Todos os componentes devem ser vinculados ao RTL da mesma maneira.
Se você desenvolver seu próprio aplicativo Turbo Vision, certifique -se de ativar os seguintes sinalizadores do compilador, ou então você receberá erros de compilação ao incluir <tvision/tv.h> :
/permissive-
/Zc:__cplusplus
Se você usar o Turbo Vision como um submódulo CMake, esses sinalizadores serão ativados automaticamente.
Nota: O Turbo Vision usa setlocale para definir as funções RTL no modo UTF-8. Isso não funcionará se você usar uma versão antiga do RTL.
Com o RTL estaticamente vinculado, e se o UTF-8 for suportado no setlocale , os aplicativos Turbo Vision serão portáteis e funcionam por padrão no Windows Vista e posteriormente .
Depois que seu ambiente Mingw é configurado corretamente, a construção é feita de maneira semelhante ao Linux:
cmake . -B ./build -G " MinGW Makefiles " -DCMAKE_BUILD_TYPE=Release &&
cmake --build ./build No exemplo acima, libtvision.a e todos os exemplos estão em ./build se TV_BUILD_EXAMPLES a opção estiver ON (o padrão).
Se você deseja vincular um aplicativo contra o Turbo Vision, basta adicionar -L./build/lib -ltvision ao seu vinculador e -I./include ao seu compilador
O Turbo Vision ainda pode ser construído como uma biblioteca DOS ou Windows com Borland C ++. Obviamente, não há suporte para Unicode aqui.
Posso confirmar que o processo de construção funciona com:
Você pode enfrentar problemas diferentes, dependendo do seu ambiente de construção. Por exemplo, o conjunto Turbo precisa de um patch para funcionar no Windows 95. No Windows XP, tudo parece funcionar bem. No Windows 10, a criação pode emitir o erro Fatal: Command arguments too long , que podem ser corrigidos, atualizando a fabricação para aqueles com Borland C ++ 5.x.
Sim, isso funciona no Windows 10 de 64 bits. O que não funciona é o instalador de C ++ Borland, que é um aplicativo de 16 bits. Você terá que executá -lo em outro ambiente ou tentar sua sorte com o WineVDM.
Um Makefile de Borland pode ser encontrado no diretório project . A construção pode ser feita fazendo:
cd project
make.exe < options > Onde <options> pode estar:
-DDOS32 para aplicativos DPMI de 32 bits (que ainda funcionam em janelas de 64 bits).-DWIN32 para aplicações nativas de Win32 nativas de 32 bits (não é possível para o TVDEMO, que se baseia no farcoreleft() e em outras antiguidades).-DDEBUG para criar versões de depuração do aplicativo e da biblioteca.-DTVDEBUG para vincular os aplicativos à versão de depuração da biblioteca.-DOVERLAY , -DALIGNMENT={2,4} , -DEXCEPTION , -DNO_STREAMABLE , -DNOTASM para coisas que eu nunca usei, mas apareceu nos makefiles originais. Isso compilará a biblioteca em um diretório LIB próximo ao project e compilará executáveis para os aplicativos de demonstração em seus respectivos examples/* diretórios.
Sinto muito, o root makefile assume que é executado no diretório project . Você ainda pode executar os makefiles originais diretamente (na source/tvision e examples/* ) se desejar usar configurações diferentes.
O Turbo Vision pode ser construído e instalado usando o gerenciador de dependência VCPKG:
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
./vcpkg integrate install
./vcpkg install tvision A porta tvision no VCPKG é mantida atualizada pelos membros da equipe da Microsoft e pelos colaboradores da comunidade. Se você achar que está desatualizado, crie uma solicitação de problema ou puxe no repositório VCPKG.
Se você escolher o sistema de construção cmake para sua aplicação, existem duas maneiras principais de vincular a Visão Turbo:
Instalando a visão turbo e importando -a com find_package . A instalação depende do tipo de gerador:
Primeiro, decida um prefixo de instalação. O padrão funcionará pronto para uso, mas geralmente requer privilégios de administrador. Nos sistemas UNIX, você pode usar $HOME/.local . No Windows, você pode usar qualquer caminho personalizado que desejar, mas precisará adicioná -lo à variável de ambiente CMAKE_PREFIX_PATH ao criar seu aplicativo.
Para geradores mono-config ( Unix Makefiles , Ninja ...), você só precisa construir e instalar uma vez:
cmake . -B ./build # '-DCMAKE_INSTALL_PREFIX=...' to override the install prefix.
cmake --build ./build
cmake --install ./build Para geradores de múltiplos configurações ( Visual Studio , Ninja Multi-Config ...), você deve criar e instalar todas as configurações:
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 Então, nos CMakeLists.txt do seu aplicativo, você pode importá -lo assim:
find_package (tvision CONFIG)
target_link_libraries (my_application tvision::tvision) Tenha uma visão turbo em um submódulo em seu repositório e importe -o com add_subdirectory :
add_subdirectory (tvision) # Assuming Turbo Vision is in the 'tvision' directory.
target_link_libraries (my_application tvision) Em ambos os casos, <tvision/tv.h> estará disponível no caminho do seu aplicativo durante a compilação, e seu aplicativo será vinculado às bibliotecas necessárias (NCURSses, GPM ...) automaticamente.
tvedit .~/ para $HOME .stdin / stdout / stderr não interfere na E / S do terminal.Existem algumas variáveis ambientais que afetam o comportamento de todas as aplicações de visão turbo:
TVISION_MAX_FPS : taxa máxima de atualização, padrão 60 . Isso pode ajudar a manter a suavidade nos emuladores terminais com manuseio não eficiente de caracteres de desenho de caixa. Os valores especiais para esta opção são 0 , para desativar a limitação da taxa de atualização e -1 , para realmente atrair para o terminal em todas as chamadas para THardwareInfo::screenWrite (útil quando depuração).
TVISION_CODEPAGE : o conjunto de caracteres usado internamente pela Turbo Vision para traduzir ASCII estendido em Unicode. Os valores válidos no momento são 437 e 850 , sendo 437 o padrão, embora a adição de mais seja muito pouco esforço.
win32-input-mode da Conpty (disponível no WSL).TIOCLINUX ) e mouse (via GPM) no console Linux.stderr é um TTY, as mensagens gravadas são redirecionadas para um buffer para impedir que elas estraguem a tela e eventualmente são impressas no console ao sair ou suspender o aplicativo.stderr falharão assim que o buffer estiver cheio. Se você deseja preservar todo o stderr , basta redirecioná -lo para um arquivo da linha de comando com 2> .As seguintes variáveis de ambiente também são levadas em consideração:
TERM : Ncurses o usa para determinar os recursos do terminal. É definido automaticamente pelo emulador de terminal.
COLORTERM : Quando definido como truecolor ou 24bit , o Turbo Vision assumirá que o emulador de terminal suporta cores de 24 bits. Ele é definido automaticamente pelos emuladores de terminais que o suportam.
ESCDELAY : o número de milissegundos para esperar depois de receber uma Pression Press, padrão 10 . Se outra tecla for pressionada durante esse atraso, ela será interpretada como uma combinação de teclas ALT+. O uso de um valor maior é útil quando o terminal não suporta a tecla ALT.
TVISION_USE_STDIO : Quando não está vazio, a E / S do terminal é realizada através stdin / stdout , para que possa ser redirecionado do shell. Por padrão, o Turbo Vision executa a E/S de terminal através /dev/tty , permitindo que o usuário redirecte stdin , stdout e stderr para suas necessidades, sem afetar a estabilidade do aplicativo.
Por exemplo, o seguinte deixará de out.txt vazio:
tvdemo | tee out.txt Enquanto o seguinte despejará todas as seqüências de fuga e texto impressos pelo aplicativo em out.txt :
TVISION_USE_STDIO=1 tvdemo | tee out.txtO seguinte não está disponível ao compilar com Borland C ++:
wchar_t . NOTA: O Turbo Vision grava o texto UTF-8 diretamente no console do Windows. Se o console estiver definido no modo herdado e a fonte Bitmap estiver sendo usada, os caracteres Unicode não serão exibidos corretamente (foto). Para evitar isso, o Turbo Vision detecta essa situação e tenta alterar a fonte do console para Consolas ou Lucida Console .
A seguir, estão os novos recursos não disponíveis no lançamento do Turbo Vision por Borland ou nas portas de código aberto anteriores (Sigala, Set):
TInputLine S não rola mais a exibição de texto no foco/foco, permitindo que o texto relevante permaneça visível.TFileViewer ( tvdemo ) e TEditor ( tvedit ). TEditor preserva a linha que termina em arquivo salvar, mas todos os arquivos recém -criados usam CRLF por padrão.TEditor : menu de contexto em clique com o botão direito do mouse.TEditor : arraste Roll com o botão do mouse do meio.TEditor , TInputLine : Exclua palavras inteiras com kbAltBack , kbCtrlBack e kbCtrlDel .TEditor : A chave inicial alterna entre o início da linha e o início do texto recuado.TEditor : Suporte para arquivos Maiores que 64 Kib em construções de 32 ou 64 bits.tvdemo : Applet de visualizador de eventos útil para depuração de eventos.tvdemo : Opção para alterar o padrão de segundo plano. As gravações da tela são tamponadas e geralmente são enviadas ao terminal uma vez para todas as iterações do loop de eventos ativas (consulte também TVISION_MAX_FPS ). Se você precisar atualizar a tela durante um loop ocupado, poderá usar TScreen::flushScreen() .
TDrawBuffer não é mais uma matriz de comprimento fixo e seus métodos impedem os acessos da matriz do passado. Portanto, o código antigo que contém comparações contra sizeof(TDrawBuffer)/sizeof(ushort) não é mais válido; Tais verificações devem ser removidas.
TApplication agora fornece dosShell() , cascade() e tile() , e lida com cmDosShell , cmCascade e cmTile por padrão. Essas funções podem ser personalizadas substituindo getTileRect() e writeShellMsg() . Este é o mesmo comportamento da versão Pascal.
Suporte da roda do mouse: New Mouse Event evMouseWheel . A mwRight mwUp roda é mwLeft no mwDown event.mouse.wheel de campo.
Suporte ao botão do mouse do meio: sinalizador de botão novo mbMiddleButton .
O campo buttons nos eventos evMouseUp não está mais vazio. Agora indica qual botão foi liberado.
Suporte ao clique triplo: Novo sinalizador de eventos do mouse meTripleClick .
Os métodos TRect move , grow , intersect e Union agora retornam TRect& em vez de ser void para que possam ser acorrentados.
TOutlineViewer agora permite que o nó raiz tenha irmãos.
Nova função ushort popupMenu(TPoint where, TMenuItem &aMenu, TGroup *receiver=0) que gera um TMenuPopup na área de trabalho. Consulte source/tvision/popupmnu.cpp .
Novo Método Virtual TMenuItem& TEditor::initContextMenu(TPoint p) que determina as entradas do menu de contexto do clique com o botão direito do mouse no TEditor .
fexpand agora pode pegar um segundo parâmetro relativeTo .
NOVA CLASSE TStringView , inspirada por std::string_view .
TStringView . TStringView é compatível com std::string_view , std::string e const char * (até nullptr ). Nova classe TSpan<T> , inspirada por std::span .
Novas classes TDrawSurface e TSurfaceView , consulte <tvision/surface.h> .
Os subsistemas de integração do sistema ( THardwareInfo , TScreen , TEventQueue ...) agora são inicializados ao construir uma TApplication pela primeira vez, e não antes main . Eles ainda são destruídos na saída de main .
Novo método TVMemMgr::reallocateDiscardable() , que pode ser usado ao longo allocateDiscardable e freeDiscardable .
Novo método TView::textEvent() que permite receber texto de maneira eficiente, consulte a interação da área de transferência.
Nova classe TClipboard , consulte a interação da área de transferência.
Suporte Unicode, consulte Unicode.
Suporte a cores verdadeiro, veja cores estendidas.
Novo método static void TEventQueue::waitForEvents(int timeoutMs) que podem bloquear para os milissegundos timeoutMs aguardando eventos de entrada. Um timeoutMs negativo pode ser usado para esperar indefinidamente. Se ele bloquear, ele terá o efeito colateral das atualizações da tela de descarga (via TScreen::flushScreen() ). Ele é chamado pelo TProgram::getEvent() com static int TProgram::eventTimeoutMs (padrão 20 ) como argumento para que o loop do evento não se transforme em um loop ocupado que consome 100% de CPU.
Novo método static void TEventQueue::wakeUp() , que faz com que o loop do evento retome a execução se estiver bloqueado no TEventQueue::waitForEvents() . Esse método é seguro para threads, pois seu objetivo é desbloquear o loop de eventos dos threads secundários.
Novo método void TView::getEvent(TEvent &, int timeoutMs) , que permite aguardar um evento com um tempo limite fornecido pelo usuário (em vez de TProgram::eventTimeoutMs ).
Agora é possível especificar uma largura máxima do texto ou a contagem máxima de caracteres na TInputLine . Isso é feito através de um novo parâmetro no construtor do TInputLine , ushort limitMode , que controla como o segundo parâmetro do construtor, uint limit , deve ser tratado. As constantes ilXXXX definem os possíveis valores do limitMode :
ilMaxBytes (o padrão): o texto pode estar ativo para limit bytes de comprimento, incluindo o terminador nulo.ilMaxWidth : O texto pode estar em alta para limit as colunas de largura.ilMaxChars : o texto pode conter para limit caracteres ou grafemas que não sejam de combinação. Novas funções que permitem obter os nomes das constantes da Turbo Vision em tempo de execução (por exemplo, evCommand , kbShiftIns , etc.):
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); Nova classe TKey , que pode ser usada para definir novas combinações de chaves (por exemplo, Shift+Alt+Up ), especificando um código -chave e uma máscara de modificadores -chave:
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 ();Novos métodos que permitem o uso de eventos cronometrados:
TTimerId TView::setTimer ( uint timeoutMs, int periodMs = - 1 );
void TView::killTimer (TTimerId id); setTimer inicia um temporizador que primeiro será lançado nos timeoutMs milissegundos e, em seguida, a cada periodMs milissegundos.
Se periodMs for negativo, o cronômetro apenas um único tempo e será limpo automaticamente. Caso contrário, continuará com o tempo periodicamente até que killTimer seja invocado.
Quando um temporizador se sai, um evento evBroadcast com o comando cmTimerExpired é emitido e message.infoPtr é definido como o TTimerId do cronômetro expirado.
Os eventos de tempo limite são gerados no TProgram::idle() . Portanto, eles são processados apenas quando não há eventos de teclado ou mouse disponíveis.
Você encontrará algumas capturas de tela aqui. Sinta -se à vontade para adicionar o seu!
Se você souber de qualquer aplicativo Turbo Vision cujo código -fonte não tenha sido perdido e isso pode se beneficiar disso, informe -me.
Se o seu aplicativo for baseado neste projeto e você gostaria que ele apareça na lista a seguir, informe -me.
A API do Turbo Vision foi estendida para permitir o recebimento de entrada Unicode e exibindo o texto Unicode. A codificação suportada é UTF-8, por vários motivos:
char * ), portanto, não requer modificações intrusivas no código existente.Observe que, quando construído com Borland C ++, o Turbo Vision não suporta Unicode. No entanto, isso não afeta a maneira como os aplicativos de visão turbo são gravados, pois as extensões da API são projetadas para permitir o código agnóstico da codificação.
A maneira tradicional de obter texto de um evento de imprensa de chaves é a seguinte:
// '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 ;
// ...
}
} Algumas das classes de visão turbo existentes que lidam com a entrada de texto ainda dependem dessa metodologia, que não mudou. Os caracteres de bytes únicos, quando representáveis na correção de código atual, continuam disponíveis no ev.keyDown.charScan.charCode .
O Suporte Unicode consiste em dois novos campos no ev.keyDown (que é um struct KeyDownEvent ):
char text[4] , que pode conter o que foi lido no terminal: geralmente uma sequência UTF-8, mas possivelmente qualquer tipo de dados brutos.uchar textLength , que é o número de bytes de dados disponíveis no text , de 0 a 4. Observe que a sequência text não é terminada nula. Você pode obter um TStringView de um KeyDownEvent com o método getText() .
Portanto, um caractere unicode pode ser recuperado do TEvent da seguinte maneira:
switch (ev.keyDown.keyCode) {
// ...
default : {
std::string_view sv = ev. keyDown . getText ();
processText (sv);
}
} Vamos ver de outra perspectiva. Se os tipos de usuário ñ , um TEvent será gerado com a seguinte estrutura keyDown :
KeyDownEvent {
union {
. keyCode = 0xA4 ,
. charScan = CharScanType {
. charCode = 164 ( ' ñ ' ), // In CP437
. scanCode = 0
}
},
. controlKeyState = 0x200 (kbInsState),
. text = { ' xC3 ' , ' xB1 ' }, // In UTF-8
. textLength = 2
} No entanto, se eles digitar € o seguinte acontecerá:
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
} Se um atalho de tecla for pressionado, text estará vazio:
KeyDownEvent {
union {
. keyCode = 0xB (kbCtrlK),
. charScan = CharScanType {
. charCode = 11 ( ' ♂ ' ),
. scanCode = 0
}
},
. controlKeyState = 0x20C (kbCtrlShift | kbInsState),
. text = {},
. textLength = 0
}Portanto, em resumo: as visualizações projetadas sem a entrada Unicode em mente continuarão funcionando exatamente como fizeram antes, e as visualizações que desejam ter consciência do Unicode não terão problemas para ser assim.
O design original do Turbo Vision usa 16 bits para representar uma célula de tela - 8 bits para um caractere e 8 bits para atributos de cores do BIOS.
Um novo tipo TScreenCell é definido em <tvision/scrncell.h> que é capaz de manter um número limitado de pontos de codePoints UTF-8, além de atributos estendidos (negrito, sublinhado, itálico ...). No entanto, você não deve escrever um texto em um TScreenCell diretamente, mas use as funções da API com reconhecimento de unicode.
Um personagem fornecido como argumento para qualquer uma das funções da API da Turbo Vision que lidam com a exibição de texto é interpretada da seguinte maneira:
0x00 a 0xFF são interpretados como caracteres no codepage ativo. Por exemplo, 0x7F é exibido como ⌂ e 0xF0 como ≡ se estiver usando CP437. Como exceção, 0x00 é sempre exibido como um espaço regular. Esses personagens são todos uma coluna de largura. Por exemplo, a sequência "╔[xFE]╗" pode ser exibida como ╔[■]╗ . Isso significa que os caracteres de desenho de caixa podem ser misturados com o UTF-8 em geral, o que é útil para compatibilidade com versões anteriores. Se você confiar nesse comportamento, poderá obter resultados inesperados: por exemplo, "xC4xBF" é uma sequência UTF-8 válida e é exibida como Ŀ em vez de ─┐ .
Uma das questões do suporte do Unicode é a existência de caracteres de largura dupla e caracteres combinados. Isso conflita com a suposição original da Turbo Vision de que a tela é uma grade de células ocupadas por um único caractere cada. No entanto, esses casos são tratados da seguinte maneira:
Os caracteres de largura dupla podem ser desenhados em qualquer lugar da tela e nada de ruim acontece se eles se sobrepor parcialmente com outros caracteres.
Os caracteres de largura zero sobreposem o caractere anterior. Por exemplo, a sequência में consiste no caractere de largura única म e nos caracteres combinados े e ं . Nesse caso, três pontos de código Unicode estão ajustados na mesma célula.
O ZERO WIDTH JOINER ( U+200D ) é sempre omitido, pois complica demais as coisas. Por exemplo, ele pode girar uma string como "??" (4 colunas de largura) em "??" (2 colunas de largura). Nem todos os emuladores terminais respeitam o ZWJ; portanto, para produzir resultados previsíveis, a Visão Turbo imprimirá os dois "??" e "??" como ?? .
Nenhuma falha gráfica notável ocorrerá enquanto seu emulador de terminal respeitar as larguras do caractere, medidas por wcwidth .
Aqui está um exemplo desses personagens no editor de texto turbo: 
A maneira usual de escrever para a tela é usando TDrawBuffer . Alguns métodos foram adicionados e outros mudaram seu significado:
void TDrawBuffer::moveChar ( ushort indent, char c, TColorAttr attr, ushort count);
void TDrawBuffer::putChar ( ushort indent, char c); c é sempre interpretado como um caractere na seleção de código ativo.
ushort TDrawBuffer::moveStr ( ushort indent, TStringView str, TColorAttr attr);
ushort TDrawBuffer::moveCStr ( ushort indent, TStringView str, TAttrPair attrs); str é interpretado de acordo com as regras expostas anteriormente.
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 é interpretado de acordo com as regras expostas anteriormente, mas:
maxWidth especifica a quantidade máxima de texto que deve ser copiada do str , medida na largura do texto (não em bytes).strOffset especifica a posição inicial no str de onde copiar, medido na largura do texto (não em bytes). Isso é útil para rolagem horizontal. Se strOffset apontar para o meio de um caractere de largura dupla, um espaço será copiado em vez da metade direita do personagem de largura dupla, pois não é possível fazer isso.Os valores de retorno são o número de células no tampão que foram realmente preenchidas com texto (que é o mesmo que a largura do texto copiado).
void TDrawBuffer::moveBuf ( ushort indent, const void *source, TColorAttr attr, ushort count); O nome desta função é enganoso. Mesmo em sua implementação original, source é tratada como uma string. Portanto, é equivalente a moveStr(indent, TStringView((const char*) source, count), attr) .
Existem outras funções úteis com consciência de unicode:
int cstrlen (TStringView s); Retorna o comprimento exibido de s de acordo com as regras acima mencionadas, descartando ~ caracteres.
int strwidth (TStringView s); // New Retorna o comprimento exibido de s .
No Borland C ++, esses métodos assumem uma codificação de bytes de um único e todos os caracteres sendo uma coluna de largura. Isso possibilita escrever métodos de draw() e handleEvent() que funcionam nas duas plataformas sem um único #ifdef .
As funções acima são implementadas usando as funções do espaço para nome do TText , outra extensão da API. Você terá que usá -los diretamente se desejar preencher objetos TScreenCell com texto manualmente. Para dar um exemplo, abaixo estão algumas das funções TText . Você pode encontrar todos eles com descrições completas em <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); Para desenhar buffers TScreenCell para uma visão, os seguintes métodos estão disponíveis:
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 É o mais simples possível. Vamos modificar hello.cpp da seguinte maneira:
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 )
);
}Aqui está o que parece:

draw() A seguir, é apresentado um trecho de uma antiga implementação do TFileViewer::draw() (parte do aplicativo tvdemo ), que não desenha o texto Unicode corretamente:
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 ); Tudo o que faz é mover parte de uma string nas fileLines para b , que é um TDrawBuffer . delta é um TPoint que representa o deslocamento de rolagem na visualização de texto e i é o índice da linha visível que está sendo processada. c é a cor do texto. Algumas questões estão presentes:
TDrawBuffer::moveStr(ushort, const char *, TColorAttr) leva uma string de terminado nulo. Para passar por uma substring da linha atual, uma cópia é feita na matriz s , correndo o risco de uma excesso de buffer. O caso em que a linha não se encaixa no s não é tratada; portanto, na maioria dos caracteres maxLineLenght será copiada. Além disso, um caractere multibyte próximo à posição maxLineLength pode ser copiado incompletamente e ser exibido como lixo.delta.x é a primeira coluna visível. Com o texto codificado por multibyte, não é mais verdade que essa coluna começa na posição delta.x na sequência.Abaixo está uma versão corrigida do código acima que lida com o Unicode corretamente:
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 ); A sobrecarga de moveStr usada aqui é TDrawBuffer::moveStr(ushort indent, TStringView str, TColorAttr attr, ushort width, ushort begin) . Essa função não apenas fornece suporte a Unicode, mas também nos ajuda a escrever código mais limpo e superar algumas das limitações previamente presentes:
maxLineLength .moveStr cuida da impressão da string a partir da coluna delta.x . Nem precisamos nos preocupar com quantos bytes correspondem às colunas 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. Por exemplo:
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. Por exemplo:
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. Por exemplo:
// 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 . Por exemplo: 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++:
| Conceito | Layout in Borland C++ | Layout in modern platforms |
|---|---|---|
| Color Attribute | uchar . A BIOS color attribute. | struct TColorAttr . |
| Cor | 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.