Ein moderner Port von Turbo Vision 2.0, der klassische Rahmen für textbasierte Benutzeroberflächen. Jetzt plattformübergreifend und mit Unicode-Unterstützung.

Ich habe dies Ende 2018 als persönliches Projekt begonnen. Bis Mai 2020 hielt ich es für sehr nahe an der Parität mit dem Original und beschloss, es offen zu machen.
Die ursprünglichen Ziele dieses Projekts waren:
An einem Punkt dachte ich, ich hätte genug getan und dass alle Versuche, die Bibliothek zu überarbeiten und ihre ursprünglichen Einschränkungen zu überwinden, entweder die API erweitern oder die Rückwärtskompatibilität brechen und dass ein großes Umschreiben am wahrscheinlichsten erforderlich wäre.
Zwischen Juli und August 2020 fand ich jedoch den Weg, um vollwertige Unicode-Unterstützung in die vorhandene Architektur zu integrieren, schrieb den Turbo-Texteditor und stellte auch die neuen Funktionen unter Windows zur Verfügung. Ich bin zuversichtlich, dass Turbo Vision jetzt viele der Erwartungen moderner Benutzer und Programmierer erfüllen kann.
Der ursprüngliche Standort dieses Projekts ist https://github.com/magiBlot/tvision.
Viel hat sich verändert, seit Borland Anfang der 90er Jahre Turbo Vision geschaffen hat. Viele GUI -Tools trennen heute die Spezifikation der Verhaltensspezifikation, verwenden sicherere oder dynamische Sprachen, die nicht auf Fehler segfault sind und entweder parallele oder asynchrone Programmierungen oder beides unterstützen.
Turbo Vision zeichnet sich in keiner dieser Auszeichnungen aus, aber es überwindet sicherlich viele der Probleme, mit denen Programmierer noch heute beim Schreiben von Terminalanwendungen konfrontiert sind:
Vergessen Sie die Terminalfunktionen und direkte E/O -Terminal. Beim Schreiben einer Turbo -Vision -Anwendung müssen Sie sich nur um Ihre Bewerbung kümmern, die Sie sich verhalten und aussehen sollen - es ist keine Notwendigkeit, in Ihrem Code Problemumgehungen hinzuzufügen. Turbo Vision versucht das Beste, um in allen Umgebungen die gleichen Ergebnisse zu erzielen. Zum Beispiel: Um eine helle Hintergrundfarbe für die Linux -Konsole zu erhalten, muss das Blink -Attribut festgelegt werden. Turbo Vision tut dies für Sie.
Wiederverwenden, was bereits getan wurde. Turbo Vision bietet viele Widget-Klassen (auch als Ansichten bezeichnet), einschließlich resizierbarer, überlappender Fenster, Pulldown-Menüs, Dialogfelder, Schaltflächen, Bildlaufleisten, Eingabefelden, Kontrollkästchen und Optionsschalttasten. Sie können diese verwenden und erweitern; Aber selbst wenn Sie es vorziehen, Ihre eigene zu erstellen, behandelt Turbo Vision bereits die Versendung von Events, die Anzeige von Unicode -Charakteren mit Vollbreiten usw.: Sie müssen keine Zeit damit verschwenden, etwas davon umzuschreiben.
Können Sie sich vorstellen, eine textbasierte Schnittstelle zu schreiben, die sowohl unter Linux als auch unter Windows (und somit plattformübergreifend ist) außerhalb des Boxs funktioniert, ohne #ifdef s? Turbo Vision macht dies möglich. Erstens: Turbo Vision verwendet ständig char , anstatt sich auf die implementierungsdefinierte und plattformabhängige wchar_t oder TCHAR zu verlassen. Zweitens: Dank an die UTF-8-Unterstützung in setlocale in den letzten Versionen von Microsofts RTL funktioniert der Code wie folgt:
std::ifstream f ( "コンピュータ.txt " ); // On Windows, the RTL converts this to the system encoding on-the-fly. hello können mit tvedit Turbo Vision für C ++ - Benutzerhandbuch beginnen und tvdemo die Beispielanwendungen ansehen. Sobald Sie die Grundlagen erfasst haben, schlage ich vor, dass Sie sich den Programmierleitfaden Turbo Vision 2.0 ansehen, der meiner Meinung nach intuitiver und einfacher zu verstehen ist, obwohl sie Pascal verwenden. Bis dahin interessieren Sie sich wahrscheinlich für das Beispiel palette , das eine detaillierte Beschreibung der Verwendung von Paletten enthält.
Vergessen Sie nicht, auch die Funktionen und API -Änderungen zu überprüfen.
Dieses Projekt hat vorerst keine stabilen Veröffentlichungen. Wenn Sie Entwickler sind, versuchen Sie, sich an das neueste Commit zu halten und alle Probleme zu melden, die Sie beim Upgrade finden.
Wenn Sie nur die Demo -Anwendungen testen möchten:
examples-dos32.zip : 32-Bit-ausführbare ausführbare mit Borland C ++. Keine Unicode -Unterstützung.examples-x86.zip : 32-Bit-ausführbare ausführbare mit MSVC erstellte. Windows Vista oder später erforderlich.examples-x64.zip : 64-Bit-ausführbare Ausführungsfähigkeiten, die mit MSVC erstellt wurden. x64 Windows Vista oder später erforderlich. Turbo Vision kann als statische Bibliothek mit CMake und GCC/Clang gebaut werden.
cmake . -B ./build -DCMAKE_BUILD_TYPE=Release && # Could also be 'Debug', 'MinSizeRel' or 'RelWithDebInfo'.
cmake --build ./build # or `cd ./build; make` CMAKE -Versionen älter als 3.13 unterstützen die Option -B möglicherweise nicht. Sie können stattdessen Folgendes ausprobieren:
mkdir -p build ; cd build
cmake .. -DCMAKE_BUILD_TYPE=Release &&
cmake --build .Das obige erzeugt die folgenden Dateien:
libtvision.a , die Turbo Vision Library.hello , tvdemo , tvedit , tvdir , die mit der ursprünglichen Turbo -Vision gebündelt waren (obwohl einige von ihnen einige Verbesserungen haben).mmenu und palette von Borlands technischer Unterstützung.tvhc , The Turbo Vision Help Compiler. Die Bibliothek und ausführbare Säle finden Sie in ./build .
Die Build -Anforderungen sind:
libncursesw (beachten Sie das 'w').libgpm für die Mausunterstützung auf der Linux -Konsole (optional). Wenn Ihre Verteilung separate Entwicklungspakete (z. B. libncurses-dev , libgpm-dev in Debian-basierter Distribution) bietet, installieren Sie diese auch.
Die Laufzeitanforderungen sind:
xsel oder xclip für die Unterstützung der Zwischenablage in X11 -Umgebungen.wl-clipboard für die Unterstützung der Zwischenablage in Wayland-Umgebungen. Die minimale Befehlszeile, die zum Erstellen einer Turbo Vision -Anwendung (z. B. hello.cpp mit GCC) aus der Wurzel dieses Projekts erforderlich ist, lautet:
g++ -std=c++14 -o hello hello.cpp ./build/libtvision.a -Iinclude -lncursesw -lgpmMöglicherweise brauchen Sie auch:
-Iinclude/tvision Wenn Ihre Anwendung Turbo Vision 1.x verwendet ( #include <tv.h> anstelle von #include <tvision/tv.h> ).
-Iinclude/tvision/compat/borland Wenn Ihre Bewerbung Borland -Header enthält ( dir.h , iostream.h usw.).
Auf Gentoo (und möglicherweise andere): -ltinfow wenn sowohl libtinfo.so als auch libtinfow.so in Ihrem System verfügbar sind. Andernfalls können Sie beim Ausführen von Turbo Vision -Anwendungen einen Segmentierungsfehler erhalten (#11). Beachten Sie, dass tinfo mit ncurses gebündelt ist.
-lgpm ist nur erforderlich, wenn Turbo Vision mit libgpm -Unterstützung gebaut wurde.
Die Abwärtskompatibilitätsheader in include/tvision/compat/borland emulieren die Borland C ++ RTL. Der Quellcode von Turbo Vision hängt immer noch von ihnen ab, und sie könnten nützlich sein, wenn sie alte Anwendungen portieren. Dies bedeutet auch, dass die Aufnahme tvision/tv.h mehrere std -Namen in den globalen Namespace bringt.
Der Build -Prozess mit MSVC ist etwas komplexer, da mehr Optionen zur Auswahl stehen. Beachten Sie, dass Sie unterschiedliche Build -Verzeichnisse für verschiedene Zielarchitekturen benötigen. Zum Beispiel, um optimierte Binärdateien zu generieren:
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'. Im obigen Beispiel werden tvision.lib und die Beispielanwendungen unter ./build/Release platziert.
Wenn Sie Turbo Vision statisch mit der Laufzeitbibliothek von Microsoft ( /MT anstelle von /MD ) verknüpfen möchten, aktivieren Sie die Option TV_USE_STATIC_RTL ( -DTV_USE_STATIC_RTL=ON Beim Anruf cmake ).
Wenn Sie eine Anwendung gegen Turbo Vision in Verbindung bringen möchten, beachten Sie, dass MSVC Sie nicht mit /MT mit /MD oder mit nicht-debug-Binärdateien debuggen können. Alle Komponenten müssen auf die gleiche Weise mit dem RTL verknüpft werden.
Wenn Sie Ihre eigene Turbo Vision -Anwendung entwickeln, sollten Sie die folgenden Compiler -Flags aktivieren. Andernfalls erhalten Sie Kompilierungsfehler, wenn Sie <tvision/tv.h> enthalten.
/permissive-
/Zc:__cplusplus
Wenn Sie Turbo Vision als CMake -Submodul verwenden, werden diese Flags automatisch aktiviert.
Hinweis: Turbo Vision verwendet setlocale , um die RTL-Funktionen im UTF-8-Modus festzulegen. Dies funktioniert nicht, wenn Sie eine alte Version des RTL verwenden.
Mit dem in setlocale statisch verknüpften RTL in Verbindung und UTF-8 sind Turbo Vision-Anwendungen tragbar und funktionieren standardmäßig unter Windows Vista und später .
Sobald Ihre Mingw -Umgebung ordnungsgemäß eingerichtet ist, erfolgt die Erstellung auf ähnliche Weise wie Linux:
cmake . -B ./build -G " MinGW Makefiles " -DCMAKE_BUILD_TYPE=Release &&
cmake --build ./build Im obigen Beispiel libtvision.a und alle Beispiele sind in ./build if TV_BUILD_EXAMPLES option ON (der Standard).
Wenn Sie eine Anwendung gegen Turbo Vision verlinken möchten, fügen Sie einfach -L./build/lib -ltvision zu Ihrem Linker und -I./include zu Ihrem Compiler hinzu
Turbo Vision kann immer noch entweder als DOS- oder Windows -Bibliothek mit Borland C ++ gebaut werden. Offensichtlich gibt es hier keine Unicode -Unterstützung.
Ich kann bestätigen, dass der Build -Prozess mit:
In Abhängigkeit von Ihrer Bauumgebung können Sie unterschiedliche Probleme haben. Zum Beispiel benötigt Turbo Assembler einen Patch, um unter Windows 95 zu arbeiten. Unter Windows XP scheint alles gut zu funktionieren. Unter Windows 10 kann Make den Fehler Fatal: Command arguments too long , die durch Upgrade auf die mit Borland C ++ 5.x gebündelte Make -Marke behoben werden können.
Ja, dies funktioniert auf 64-Bit-Windows 10. Was nicht funktioniert, ist das Borland C ++-Installateur, eine 16-Bit-Anwendung. Sie müssen es in einer anderen Umgebung betreiben oder Ihr Glück mit Winevdm versuchen.
Ein Borland Makefile finden Sie im project . Build kann durch tun:
cd project
make.exe < options > Wo <options> können:
-DDOS32 für 32-Bit-DPMI-Anwendungen (die noch an 64-Bit-Fenstern funktionieren).-DWIN32 für 32-Bit-native Win32-Anwendungen (nicht möglich für TVDEMO, das auf farcoreleft() und anderen Antiquitäten beruht).-DDEBUG , um Debug -Versionen der Anwendung und der Bibliothek zu erstellen.-DTVDEBUG um die Anwendungen mit der Debug -Version der Bibliothek zu verknüpfen.-DOVERLAY , -DALIGNMENT={2,4} , -DEXCEPTION , -DNO_STREAMABLE , -DNOTASM für Dinge, die ich nie verwendet habe, sondern in den ursprünglichen Makefiles erschien. Dadurch wird die Bibliothek neben project in ein LIB -Verzeichnis zusammengestellt und ausführbare Funktionen für die Demo -Anwendungen in ihren jeweiligen examples/* -Verzeichnissen zusammengestellt.
Es tut mir leid, das Root Makefile geht davon aus, dass sie aus dem project ausgeführt wird. Sie können die ursprünglichen Makefiles weiterhin direkt ausführen (in source/tvision und examples/* ), wenn Sie verschiedene Einstellungen verwenden möchten.
Turbo Vision kann mit dem VCPKG -Abhängigkeitsmanager erstellt und installiert werden:
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
./vcpkg integrate install
./vcpkg install tvision Der tvision -Port in VCPKG wird von Microsoft -Teammitgliedern und Community -Mitwirkenden auf dem Laufenden gehalten. Wenn Sie feststellen, dass es veraltet ist, erstellen Sie bitte eine Ausgabe oder eine Zuganfrage im VCPKG -Repository.
Wenn Sie das CMake -Build -System für Ihre Anwendung auswählen, gibt es zwei wichtigste Möglichkeiten, mit Turbo Vision zu verknüpfen:
Installieren Sie Turbo Vision und importieren Sie es mit find_package . Die Installation hängt vom Generatortyp ab:
Entscheiden Sie zunächst ein Installationspräfix. Die Standardeinstellung, die One-of-the-Box funktioniert, erfordert jedoch normalerweise Administratorrechte. Auf UNIX -Systemen können Sie stattdessen $HOME/.local verwenden. Unter Windows können Sie einen beliebigen benutzerdefinierten Pfad verwenden, den Sie gewünscht haben, aber Sie müssen ihn zur Umgebungsvariablen CMAKE_PREFIX_PATH beim Erstellen Ihrer Anwendung hinzufügen.
Für Mono-Config-Generatoren ( Unix Makefiles , Ninja ...) müssen Sie nur einmal erstellen und installieren:
cmake . -B ./build # '-DCMAKE_INSTALL_PREFIX=...' to override the install prefix.
cmake --build ./build
cmake --install ./build Für Multi-Config-Generatoren ( Visual Studio , Ninja Multi-Config ...) sollten Sie alle Konfigurationen erstellen und installieren:
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 Dann können Sie in der Anwendung CMakeLists.txt es so importieren:
find_package (tvision CONFIG)
target_link_libraries (my_application tvision::tvision) Haben Sie Turbo Vision in einem Submodul in Ihrem Repository und importieren Sie es mit add_subdirectory :
add_subdirectory (tvision) # Assuming Turbo Vision is in the 'tvision' directory.
target_link_libraries (my_application tvision) In beiden Fällen wird <tvision/tv.h> in der Kompilierung Ihrer Anwendung in den Pfad in der Kompilierung verfügbar sein, und Ihre Bewerbung wird automatisch mit den erforderlichen Bibliotheken (NCURSES, GPM ...) verknüpft.
tvedit -Anwendung ausprobieren.~/ in $HOME erweitern.stdin / stdout / stderr stört die terminale I / O nicht.Es gibt einige Umgebungsvariablen, die das Verhalten aller Turbo Vision -Anwendungen beeinflussen:
TVISION_MAX_FPS : Maximale Aktualisierungsrate, Standard 60 . Dies kann dazu beitragen, die Glättung bei terminalen Emulatoren mit einer uneingeschränkten Handhabung von Box-Drawing-Zeichen zu halten. Spezielle Werte für diese Option sind 0 , um die Begrenzung der Aktualisierungsrate zu deaktivieren, und -1 , um in jedem Aufruf an THardwareInfo::screenWrite (beim Debuggen nützlich) zum Terminal zu zeichnen.
TVISION_CODEPAGE : Das von Turbo Vision intern verwendete Zeichensatz, um erweiterte ASCII in Unicode zu übersetzen. Die momentan gültigen Werte betragen 437 und 850 , wobei 437 die Standardeinstellung sind, obwohl das Hinzufügen mehr kaum Anstrengungen erfordert.
win32-input-mode von Conpty (verfügbar in WSL).TIOCLINUX ) und Maus (über GPM) in der Linux -Konsole.stderr ein TTY ist, werden Nachrichten, die darauf geschrieben wurden, in einen Puffer umgeleitet, um zu verhindern, dass sie das Display durcheinander bringen, und werden schließlich beim Beenden oder Aussetzen der Anwendung in die Konsole gedruckt.stderr fehlschlagen, sobald der Puffer voll ist. Wenn Sie das gesamte stderr beibehalten möchten, leiten Sie sie einfach mit 2> in eine Datei aus der Befehlszeile um.Die folgenden Umgebungsvariablen werden ebenfalls berücksichtigt:
TERM : NCURSSS verwendet es, um die Endfunktionen zu bestimmen. Es wird automatisch vom Terminalemulator festgelegt.
COLORTERM : Wenn Turbo Vision auf truecolor oder 24bit eingestellt ist, wird der Terminalemulator die 24-Bit-Farbe unterstützt. Es wird automatisch von terminalen Emulatoren festgelegt, die es unterstützen.
ESCDELAY : Die Anzahl der Millisekunden zum Warten nach dem Erhalt einer ESC -Taste, standardmäßig 10 . Wenn während dieser Verzögerung eine andere Taste gedrückt wird, wird sie als Alt+-Key -Kombination interpretiert. Die Verwendung eines größeren Wertes ist nützlich, wenn das Terminal den ALT -Schlüssel nicht unterstützt.
TVISION_USE_STDIO : Wenn Sie nicht leer sind, wird die terminale E / A über stdin / stdout durchgeführt, sodass es aus der Schale umgeleitet werden kann. Standardmäßig führt Turbo Vision eine terminale E/A über /dev/tty durch, sodass der Benutzer stdin , stdout und stderr für seine Anforderungen umleiten kann, ohne die Stabilität der Anwendung zu beeinträchtigen.
Zum Beispiel wird das Folgende out.txt . Txt leer:
tvdemo | tee out.txt Während die folgenden Fluchtsequenzen und Text von der Anwendung in out.txt ausgedruckt werden:
TVISION_USE_STDIO=1 tvdemo | tee out.txtDas Folgende ist beim Kompilieren mit Borland C ++ nicht verfügbar:
wchar_t nicht verwenden müssen. Hinweis: Turbo Vision schreibt UTF-8-Text direkt an die Windows-Konsole. Wenn die Konsole im Legacy -Modus eingestellt ist und die Bitmap -Schriftart verwendet wird, werden Unicode -Zeichen nicht ordnungsgemäß angezeigt (Foto). Um dies zu vermeiden, erkennt Turbo Vision diese Situation und versucht, die Konsole -Schriftart in Consolas oder Lucida Console zu ändern.
Im Folgenden sind neue Funktionen in Borlands Veröffentlichung von Turbo Vision oder in früheren Open Source -Ports (Sigala, Set) nicht verfügbar:
TInputLine S scrollen Sie die Textanzeige nicht mehr auf Fokus/Unfokus, sodass relevante Text sichtbar bleiben kann.TFileViewer ( tvdemo ) und TEditor ( tvedit ). TEditor bewahrt die Zeile, die in der Datei speichern, aber alle neu erstellten Dateien verwenden standardmäßig CRLF.TEditor : Kontextmenü auf der rechten Maustaste.TEditor : Drücken Sie die Scrolle mit der mittleren Maustaste.TEditor , TInputLine : Löschen Sie ganze Wörter mit kbAltBack , kbCtrlBack und kbCtrlDel .TEditor : Die Heimschlüssel wechselt zwischen dem Beginn der Linie und dem Beginn des eingeregten Textes.TEditor : Unterstützung für Dateien, die größer als 64 KIB auf 32-Bit- oder 64-Bit-Builds sind.tvdemo : Event Viewer Applet nützlich für das Debuggen von Events.tvdemo : Option, um das Hintergrundmuster zu ändern. Die Schreibvorgänge werden gepuffert und werden normalerweise einmal für jede Iteration der aktiven Ereignisschleife an das Terminal gesendet (siehe auch TVISION_MAX_FPS ). Wenn Sie den Bildschirm während einer belebten Schleife aktualisieren müssen, können Sie TScreen::flushScreen() verwenden.
TDrawBuffer ist kein Array mit fester Länge mehr und seine Methoden verhindern Array-Zugriffe in der Vergangenheit. Daher ist der alte Code, der Vergleiche mit sizeof(TDrawBuffer)/sizeof(ushort) enthält, nicht mehr gültig; Solche Schecks sollten entfernt werden.
TApplication bietet nun dosShell() , cascade() und tile() und verarbeitet cmDosShell , cmCascade und cmTile standardmäßig. Diese Funktionen können angepasst werden, indem getTileRect() und writeShellMsg() überschritten wird. Dies ist das gleiche Verhalten wie in der Pascal -Version.
Support für Mausrad: Neues Mausereignis evMouseWheel . Die Radrichtung mwRight mwDown neuen event.mouse.wheel mwLeft mwUp
Unterstützung bei mittlerer Maustaste: Neue Maustaste Flag mbMiddleButton .
Das Feld buttons in evMouseUp -Ereignissen ist nicht mehr leer. Es zeigt nun an, welcher Taste veröffentlicht wurde.
Triple-Click-Support: New Mouse Event Flag meTripleClick .
TRect -Methoden move , grow , intersect und Union nun TRect& anstatt für void zu sein, damit sie gekettet werden können.
TOutlineViewer ermöglicht es dem Stammknoten nun, Geschwister zu haben.
Neue Funktion ushort popupMenu(TPoint where, TMenuItem &aMenu, TGroup *receiver=0) , das einen TMenuPopup auf dem Desktop hervorbringt. Siehe source/tvision/popupmnu.cpp .
Neue virtuelle Methode TMenuItem& TEditor::initContextMenu(TPoint p) , das die Einträge des Rechtsklick-Kontextmenüs im TEditor bestimmt.
fexpand kann jetzt einen zweiten Parameter relativeTo nehmen.
Neue Klasse TStringView , inspiriert von std::string_view .
TStringView . TStringView ist mit std::string_view , std::string und const char * (sogar nullptr ) kompatibel. Neue Klasse TSpan<T> , inspiriert von std::span .
Neue Klassen TDrawSurface und TSurfaceView , siehe <tvision/surface.h> .
Die Systemintegrations -Subsysteme ( THardwareInfo , TScreen , TEventQueue ...) werden jetzt bei der Erstellung einer TApplication erstmals initialisiert und nicht vor main . Sie werden beim Ausgang von main immer noch zerstört.
NEUE METHODE TVMemMgr::reallocateDiscardable() , die entlang allocateDiscardable und freeDiscardable verwendet werden können.
NEUE METHODE TView::textEvent() , mit dem Text effizient empfangen werden kann, siehe Interaktion mit der Zwischenablage.
Neue Klasse TClipboard , siehe Zwischenablage Interaktion.
Unicode -Unterstützung, siehe Unicode.
Wahre Farbunterstützung, siehe erweiterte Farben.
NEUE METHODE static void TEventQueue::waitForEvents(int timeoutMs) , die für timeoutMs Millisekunden auf Eingabeereignisse warten können. Ein negativer timeoutMs kann verwendet werden, um unbedingt zu warten. Wenn es blockiert, hat es den Nebeneffekt von Flushing -Bildschirmaktualisierungen (über TScreen::flushScreen() ). Es wird von TProgram::getEvent() mit static int TProgram::eventTimeoutMs (Standard 20 ) als Argument aufgerufen, damit die Ereignisschleife nicht in eine belebte Schleife wird und 100% CPU verbraucht.
NEUE METHODE static void TEventQueue::wakeUp() , wodurch die Ereignisschleife die Ausführung wieder aufnimmt, wenn sie bei TEventQueue::waitForEvents() blockiert wird. Diese Methode ist Thread-Safe, da ihr Zweck darin besteht, die Ereignisschleife von sekundären Threads zu entsperren.
NEUE METHODE void TView::getEvent(TEvent &, int timeoutMs) mit dem das Warten auf ein Ereignis mit einem von Benutzer bereitgestellten Timeout (anstelle von TProgram::eventTimeoutMs ) warten kann.
Es ist nun möglich, eine maximale Textbreite oder maximale Zeichenanzahl in TInputLine anzugeben. Dies geschieht durch einen neuen Parameter in TInputLine Konstruktor ushort limitMode , der steuert, wie der zweite Konstruktorparameter uint limit behandelt werden soll. Die ilXXXX -Konstanten definieren die möglichen Werte von limitMode :
ilMaxBytes (der Standard): Der Text kann so lange sein, dass limit lang ist, einschließlich des Null -Terminators.ilMaxWidth : Der Text kann bis zu den limit weit verbreitet sein.ilMaxChars : Der Text kann bis zur limit von Nichtkombinationszeichen oder Graphemen enthalten. Neue Funktionen, die es ermöglichen, die Namen der Konstanten von Turbo Vision zur Laufzeit zu erhalten (z. B. evCommand , kbShiftIns usw.):
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); Neue Klasse TKey , mit der neue Schlüsselkombinationen (z. B. Shift+Alt+Up ) definiert werden können, indem ein Schlüsselcode und eine Maske der Schlüsselmodifikatoren angegeben werden:
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 ();Neue Methoden, mit denen zeitgesteuerte Ereignisse verwendet werden:
TTimerId TView::setTimer ( uint timeoutMs, int periodMs = - 1 );
void TView::killTimer (TTimerId id); setTimer startet einen Timer, der erstmals in Millisekunden und dann in allen Millisekunden periodMs timeoutMs ausgeht.
Wenn periodMs negativ sind, wird der Timer nur ein einziges Mal ausgeführt und wird automatisch aufgeräumt. Andernfalls wird es regelmäßig einteilen, bis killTimer aufgerufen wird.
Wenn ein Timer ausfällt, wird ein evBroadcast -Ereignis mit dem Befehl cmTimerExpired emittiert, und message.infoPtr wird auf die TTimerId des abgelaufenen Timers eingestellt.
Timeout -Ereignisse werden in TProgram::idle() generiert. Daher werden sie nur verarbeitet, wenn keine Tastatur- oder Mausereignisse verfügbar sind.
Hier finden Sie einige Screenshots. Fühlen Sie sich frei, Ihre eigenen hinzuzufügen!
Wenn Sie Turbo Vision -Anwendungen kennen, deren Quellcode nicht verloren gegangen ist und dies davon profitieren könnte, lassen Sie es mich wissen.
Wenn Ihre Bewerbung auf diesem Projekt basiert und Sie in der folgenden Liste angezeigt werden möchten, lassen Sie es mich einfach wissen.
Die Turbo Vision API wurde erweitert, um das Empfangen von Unicode -Eingaben und das Anzeigen von Unicode -Text zu ermöglichen. Die unterstützte Codierung ist aus mehreren Gründen UTF-8:
char * ) kompatibel, sodass keine aufdringlichen Änderungen an vorhandenen Code erforderlich sind.Beachten Sie, dass Turbo Vision, wenn er mit Borland C ++ gebaut wurde, Unicode nicht unterstützt. Dies wirkt sich jedoch nicht auf die Art und Weise aus, wie Turbo Vision-Anwendungen geschrieben werden, da die API-Erweiterungen so ausgelegt sind, dass Codierungs-Agnostische Code ermöglicht.
Die traditionelle Art, Text von einem Tastendruckereignis zu erhalten, ist wie folgt:
// '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 ;
// ...
}
} Einige der vorhandenen Turbo -Vision -Klassen, die sich mit den Texteingaben befassen, hängen immer noch von dieser Methodik ab, die sich nicht geändert hat. Single-Byte-Zeichen sind, wenn sie im aktuellen Codepage dargestellt werden, weiterhin in ev.keyDown.charScan.charCode verfügbar.
Die Unterstützung von Unicode besteht aus zwei neuen Feldern in ev.keyDown (ein struct KeyDownEvent ):
char text[4] , der möglicherweise alles enthalten kann, was aus dem Terminal gelesen wurde: normalerweise eine UTF-8-Sequenz, aber möglicherweise jede Art von Rohdaten.uchar textLength , die Anzahl der in text verfügbaren Datenbytes von 0 bis 4 ist. Beachten Sie, dass die text nicht nullterminiert ist. Mit der Methode getText() können Sie eine TStringView aus einem KeyDownEvent herausholen.
Ein Unicode -Zeichen kann also auf folgende Weise von TEvent abgerufen werden:
switch (ev.keyDown.keyCode) {
// ...
default : {
std::string_view sv = ev. keyDown . getText ();
processText (sv);
}
} Mal aus einer anderen Perspektive sehen. Wenn der Benutzer Typ ñ , wird ein TEvent mit der folgenden keyDown -Struktur generiert:
KeyDownEvent {
union {
. keyCode = 0xA4 ,
. charScan = CharScanType {
. charCode = 164 ( ' ñ ' ), // In CP437
. scanCode = 0
}
},
. controlKeyState = 0x200 (kbInsState),
. text = { ' xC3 ' , ' xB1 ' }, // In UTF-8
. textLength = 2
} Wenn sie jedoch € eingeben, wird Folgendes passieren:
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
} Wenn stattdessen eine Schlüsselkombination gedrückt wird, ist text leer:
KeyDownEvent {
union {
. keyCode = 0xB (kbCtrlK),
. charScan = CharScanType {
. charCode = 11 ( ' ♂ ' ),
. scanCode = 0
}
},
. controlKeyState = 0x20C (kbCtrlShift | kbInsState),
. text = {},
. textLength = 0
}Kurz gesagt: Ansichten, die ohne Unicode-Input entworfen wurden, funktionieren weiterhin genau wie zuvor, und Ansichten, die unicode-bewusst sein möchten, werden keine Probleme haben.
Das ursprüngliche Design von Turbo Vision verwendet 16 Bit, um eine Bildschirmzelle für einen Charakter und 8 -Bit für BIOS -Farbattribute darzustellen.
Ein neuer TScreenCell Typ ist in <tvision/scrncell.h> definiert, das zusätzlich zu erweiterten Attributen (fett, unterstreich, kursiv ...) eine begrenzte Anzahl von UTF-8-CodePoints halten kann. Sie sollten jedoch keinen Text direkt in eine TScreenCell schreiben, sondern stattdessen von UNICODE-API-Funktionen verwenden.
Ein Charakter, das als Argument für eine der Turbo Vision -API -Funktionen bereitgestellt wird, die sich mit dem Anzeigen von Text befassen, wird wie folgt interpretiert:
0x00 bis 0xFF werden als Zeichen im aktiven Codepage interpretiert. Zum Beispiel wird 0x7F als ⌂ und 0xF0 als ≡ bei Verwendung von CP437 angezeigt. Ausnahmsweise wird 0x00 immer als regulärer Raum angezeigt. Diese Zeichen sind alle eine Spalte breit. Beispielsweise kann die Zeichenfolge "╔[xFE]╗" als ╔[■]╗ angezeigt werden. Dies bedeutet, dass Box-Drawing-Zeichen im Allgemeinen mit UTF-8 gemischt werden können, was für die Rückwärtskompatibilität nützlich ist. Wenn Sie sich jedoch auf dieses Verhalten verlassen, erhalten Sie möglicherweise unerwartete Ergebnisse: Zum Beispiel ist "xC4xBF" eine gültige UTF-8-Sequenz und wird als Ŀ statt ─┐ angezeigt.
Eines der Probleme der Unicode-Unterstützung ist die Existenz von Doppelbreiten und Kombination von Charakteren. Dies widerspricht der ursprünglichen Annahme von Turbo Vision, dass das Bildschirm ein Zellenraster ist, das von jeweils ein einzelnes Zeichen besetzt ist. Trotzdem werden diese Fälle auf folgende Weise behandelt:
Doppelbreite Zeichen können überall auf dem Bildschirm gezogen werden, und es passiert nichts Schlimmes, wenn sie sich teilweise mit anderen Zeichen überlappen.
Null-Breiten-Zeichen überlagern den vorherigen Charakter. Zum Beispiel besteht die Sequenz में aus dem Einzelbreitzeichen म und den kombinierten Zeichen े und ं . In diesem Fall sind drei Unicode -CodePoints in dieselbe Zelle angepasst.
Der ZERO WIDTH JOINER ( U+200D ) wird immer weggelassen, da er die Dinge zu sehr kompliziert. Zum Beispiel kann es eine Zeichenfolge wie "??" drehen (4 Spalten breit) in "??" (2 Spalten breit). Nicht alle terminalen Emulatoren respektieren den ZWJ. Um vorhersehbare Ergebnisse zu erzielen, drucken Sie beide "??" Und "??" als ?? .
Es treten keine bemerkenswerten grafischen Störungen auf, solange Ihr Terminalemulator die Zeichenbreiten respektiert, gemessen durch wcwidth .
Hier ist ein Beispiel für solche Zeichen im Turbo -Texteditor: 
Die übliche Art, auf den Bildschirm zu schreiben, besteht darin, TDrawBuffer zu verwenden. Es wurden einige Methoden hinzugefügt und andere haben ihre Bedeutung geändert:
void TDrawBuffer::moveChar ( ushort indent, char c, TColorAttr attr, ushort count);
void TDrawBuffer::putChar ( ushort indent, char c); c wird immer als Zeichen im aktiven Codepage interpretiert.
ushort TDrawBuffer::moveStr ( ushort indent, TStringView str, TColorAttr attr);
ushort TDrawBuffer::moveCStr ( ushort indent, TStringView str, TAttrPair attrs); str wird gemäß den zuvor aufgedeckten Regeln interpretiert.
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 wird gemäß den zuvor aufgedeckten Regeln interpretiert, aber:
maxWidth gibt die maximale Textmenge an, die von str kopiert werden sollte, gemessen in der Textbreite (nicht in Bytes).strOffset gibt die anfängliche Position in str an, von der er kopiert werden soll, gemessen in Textbreite (nicht in Bytes). Dies ist nützlich für das horizontale Scrollen. Wenn strOffset auf die Mitte eines Doppelbreitencharakters verweist, wird ein Raum anstelle der rechten Hälfte des Doppelbreitencharakters kopiert, da es nicht möglich ist, so etwas zu tun.Die Rückgabewerte sind die Anzahl der Zellen im Puffer, die tatsächlich mit Text gefüllt waren (was der Breite des kopierten Textes entspricht).
void TDrawBuffer::moveBuf ( ushort indent, const void *source, TColorAttr attr, ushort count); Der Name dieser Funktion ist irreführend. Selbst in seiner ursprünglichen Implementierung wird source als Zeichenfolge behandelt. So entspricht es moveStr(indent, TStringView((const char*) source, count), attr) .
Es gibt andere nützliche Unicode-bewusste Funktionen:
int cstrlen (TStringView s); Gibt die angezeigte Länge von s gemäß den oben genannten Regeln zurück, wobei ~ Zeichen verworfen werden.
int strwidth (TStringView s); // New Gibt die angezeigte Länge von s zurück.
Auf Borland C ++ nehmen diese Methoden eine Single-Byte-Codierung an und alle Zeichen sind eine Spalte breit. Dies ermöglicht das Schreiben von Coding-Agnostic draw() und handleEvent() -Methoden, die auf beiden Plattformen ohne einzelne #ifdef arbeiten.
Die obigen Funktionen werden unter Verwendung der Funktionen aus dem TText -Namespace, einer anderen API -Erweiterung, implementiert. Sie müssen sie direkt verwenden, wenn Sie TScreenCell -Objekte manuell mit Text füllen möchten. Um ein Beispiel zu geben, finden Sie unten einige der TText -Funktionen. Sie finden alle mit vollständigen Beschreibungen in <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); Zum Zeichnen TScreenCell -Puffer in eine Ansicht sind die folgenden Methoden verfügbar:
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 Es ist so einfach, wie es sein kann. Ändern wir hello.cpp wie folgt:
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 )
);
}So sieht es aus:

draw() -Methoden Das Folgende ist ein Auszug aus einer alten Implementierung von TFileViewer::draw() (Teil der tvdemo -Anwendung), die den Unicode -Text nicht ordnungsgemäß zeichnet:
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 ); Alles, was es tut, ist, einen Teil einer Zeichenfolge in fileLines in b zu bewegen, was ein TDrawBuffer ist. delta ist ein TPoint , der den Scroll -Offset in der Textansicht darstellt, und i ist der Index der sichtbaren Zeile, die verarbeitet wird. c ist die Textfarbe. Einige Probleme sind vorhanden:
TDrawBuffer::moveStr(ushort, const char *, TColorAttr) nimmt eine null-terminierte Zeichenfolge. Um eine Substring der aktuellen Linie zu übergeben, wird eine Kopie in das Array s auf das Risiko einer Pufferüberschreitung aufgenommen. Der Fall, in dem die Linie nicht in s passt, wird nicht behandelt, daher werden höchstens maxLineLenght -Zeichen kopiert. Darüber hinaus könnte ein Multibyte -Charakter in der Nähe von Position maxLineLength unvollständig kopiert und als Müll angezeigt werden.delta.x ist die erste sichtbare Spalte. Bei Multibyte-kodierter Text ist es nicht mehr wahr, dass eine solche Spalte in Position delta.x in der Zeichenfolge beginnt.Im Folgenden finden Sie eine korrigierte Version des obigen Codes, die Unicode ordnungsgemäß behandelt:
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 ); Die hier verwendete Überlastung von moveStr ist TDrawBuffer::moveStr(ushort indent, TStringView str, TColorAttr attr, ushort width, ushort begin) . Diese Funktion bietet nicht nur Unicode -Unterstützung, sondern hilft uns auch, saubereren Code zu schreiben und einige der zuvor vorhandenen Einschränkungen zu überwinden:
maxLineLength -Bytes beschränkt ist.moveStr kümmert sich um den Druck der Zeichenfolge ab Spalten delta.x . Wir müssen uns nicht einmal Sorgen machen, wie viele Bytes delta.x -Spalten entsprechen.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. Zum Beispiel:
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. Zum Beispiel:
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. Zum Beispiel:
// 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 . Zum Beispiel: 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++:
| Konzept | Layout in Borland C++ | Layout in modern platforms |
|---|---|---|
| Color Attribute | uchar . A BIOS color attribute. | struct TColorAttr . |
| Farbe | 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.