Turbo Vision 2.0的現代港口,這是基於文本的用戶界面的經典框架。現在跨平台並獲得Unicode支持。

我在2018年底開始作為一個個人項目。到2020年5月,我認為它與原始項目非常接近,並決定使其開放。
該項目的最初目標是:
在某一時刻,我認為我已經做得足夠了,並且任何嘗試修改圖書館並克服其原始限制的嘗試都需要擴展API或向後兼容,並且很可能是必要的重大重寫。
但是,在2020年7月至8月之間,我找到了將成熟的Unicode支持集成到現有體系結構中的方式,撰寫了Turbo Text Editor,並在Windows上提供了新功能。因此,我相信Turbo Vision現在可以滿足現代用戶和程序員的許多期望。
該項目的原始位置是https://github.com/magiblot/tvision。
自Borland在90年代初創造了Turbo Vision以來,發生了很多變化。如今,許多GUI工具將外觀規範與行為規範分開,使用不符合錯誤的更安全或動態語言,並支持並行或異步編程或兩者兼而有之。
Turbo Vision不會在任何一個方面表現出色,但是它肯定克服了許多程序員在編寫終端應用程序時仍面臨的許多問題:
忘記終端功能和直接終端I/O。在編寫Turbo Vision應用程序時,您需要關心的只是您想要的應用程序的表現和外觀 - 無需在代碼中添加解決方法。 Turbo Vision盡力在所有環境上產生相同的結果。例如:要在Linux控制台上獲得明亮的背景顏色,必須設置眨眼屬性。 Turbo Vision為您做到這一點。
重複使用已經做過的事情。 Turbo Vision提供許多小部件類(也稱為視圖),包括可重疊的,重疊的窗戶,下拉菜單,對話框,按鈕,滾動條,輸入框,複選框和無線電按鈕。您可以使用並擴展這些;但是,即使您更喜歡創建自己的渦輪增壓願景,已經可以處理派遣事件,顯示全width Unicode字符等:您不需要浪費時間重寫任何一個。
您能想像編寫一個基於文本的界面,該界面在Linux和Windows上都起作用(因此是跨平台),沒有#ifdef s? Turbo Vision使這成為可能。首先:Turbo Vision繼續使用char陣列,而不是依靠實現定義和依賴平台的wchar_t或TCHAR 。第二:感謝Microsoft RTL最近版本中的setlocale中的UTF-8支持,如下所示,代碼將按預期工作:
std::ifstream f ( "コンピュータ.txt " ); // On Windows, the RTL converts this to the system encoding on-the-fly. 您可以開始使用C ++用戶指南的Turbo Vision,並查看示例應用程序hello , tvdemo和tvedit 。掌握了基礎知識後,我建議您查看《 Turbo Vision 2.0編程指南》,我認為,儘管使用了Pascal,但我認為,它更加直觀,更容易理解。到那時,您可能會對palette示例感興趣,該示例包含有關如何使用調色板的詳細說明。
不要忘記查看功能和API更改部分。
該項目暫時沒有穩定的發行版。如果您是開發人員,請嘗試堅持最新的提交,並報告您在升級時發現的任何問題。
如果您只想測試演示應用程序:
examples-dos32.zip :使用Borland C ++構建的32位可執行文件。沒有Unicode支持。examples-x86.zip :使用MSVC構建的32位可執行文件。 Windows Vista或更高版本。examples-x64.zip :使用MSVC構建的64位可執行文件。 x64 Windows Vista或以後需要。 Turbo Vision可以作為帶有CMAKE和GCC/CLANG的靜態庫來構建。
cmake . -B ./build -DCMAKE_BUILD_TYPE=Release && # Could also be 'Debug', 'MinSizeRel' or 'RelWithDebInfo'.
cmake --build ./build # or `cd ./build; make` CMAKE版本以上超過3.13可能不支持-B選項。您可以嘗試以下操作:
mkdir -p build ; cd build
cmake .. -DCMAKE_BUILD_TYPE=Release &&
cmake --build .以上產生以下文件:
libtvision.a ,這是渦輪視覺庫。hello , tvdemo , tvedit , tvdir與原始的Turbo Vision捆綁在一起(儘管其中一些有一些改進)。mmenu和palette的演示應用程序。tvhc ,Turbo Vision幫助編譯器。可以在./build中找到庫和可執行文件。
構建要求是:
libncursesw (請注意'W')。libgpm用於Linux控制台(可選)上的鼠標支撐。如果您的發行版提供單獨的Devel軟件包(例如,基於Debian的發行版中的libncurses-dev , libgpm-dev ),請安裝它們。
運行時要求是:
xsel或xclip用於X11環境中的剪貼板支持。wl-clipboard用於Wayland環境中的剪貼板支撐。該項目的根源hello.cpp :
g++ -std=c++14 -o hello hello.cpp ./build/libtvision.a -Iinclude -lncursesw -lgpm您也可能需要:
-Iinclude/tvision如果您的應用程序使用Turbo Vision 1.X包含( #include <tv.h>而不是#include <tvision/tv.h> )。
-Iinclude/tvision/compat/borland如果您的應用程序包括Borland標題( dir.h , iostream.h等)。
在Gentoo(可能還有其他)上: -ltinfow ,如果您的系統中都可以使用libtinfo.so和libtinfow.so 。否則,在運行Turbo Vision應用程序(#11)時,您可能會得到細分故障。請注意, tinfo與ncurses捆綁在一起。
-lgpm僅在使用libgpm支持的情況下建立Turbo Vision時才有必要。
向後兼容的標題include/tvision/compat/borland模擬Borland C ++ RTL。 Turbo Vision的源代碼仍然取決於它們,如果移植舊應用程序,它們可能很有用。這也意味著,包括tvision/tv.h在內,將為全球名稱空間帶來幾個std名稱。
使用MSVC的構建過程更為複雜,因為還有更多選擇。請注意,您將需要不同的目標體系結構的不同構建目錄。例如,生成優化的二進製文件:
cmake . -B ./build && # Add '-A x64' (64-bit) or '-A Win32' (32-bit) to override the default platform.
cmake --build ./build --config Release # Could also be 'Debug', 'MinSizeRel' or 'RelWithDebInfo'.在上面的示例中, tvision.lib和示例應用程序將放置在./build/Release 。
如果您希望與Microsoft的運行時庫( /MT而不是/MD )靜態地鏈接Turbo Vision,請啟用TV_USE_STATIC_RTL選項( -DTV_USE_STATIC_RTL=ON調用cmake時)。
如果您想將應用程序與Turbo Vision鏈接起來,請注意,MSVC不允許您與/MD混合/MT或與非debebug二進製文件混合或調試。所有組件都必須以相同的方式鏈接到RTL。
如果您開發了自己的Turbo Vision應用程序,請確保啟用以下編譯器標誌,否則在包括<tvision/tv.h>時會遇到編譯錯誤:
/permissive-
/Zc:__cplusplus
如果您將渦輪視覺用作CMAKE子模塊,則將自動啟用這些標誌。
注意: Turbo Vision使用setlocale將RTL功能設置為UTF-8模式。如果您使用舊版本的RTL,則該工作將不起作用。
隨著靜態鏈接的RTL,如果在setlocale中支持UTF-8,則Turbo Vision應用程序是便攜式的,並且默認情況下在Windows Vista上和更高版本。
一旦正確設置了MINGW環境,就以與Linux相似的方式進行構建:
cmake . -B ./build -G " MinGW Makefiles " -DCMAKE_BUILD_TYPE=Release &&
cmake --build ./build在上面的示例中,如果TV_BUILD_EXAMPLES選項為ON (默認值),則libtvision.a和所有示例在./build中。
如果您想針對渦輪願景鏈接應用程序,只需將-L./build/lib -ltvision添加到您的鏈接器和-I./include到您的編譯器
仍然可以用Borland C ++作為DOS或Windows庫來構建Turbo Vision。顯然,這裡沒有Unicode的支持。
我可以確認構建過程可與:
根據您的構建環境,您可能會遇到不同的問題。例如,Turbo Assembler需要一個補丁以在Windows 95下工作。在Windows XP上,一切似乎都很好。在Windows 10上,MAKE可能會發出錯誤Fatal: Command arguments too long ,可以通過將其升級到與Borland C ++ 5.x捆綁的一個來固定。
是的,這在64位Windows 10上起作用。不起作用的是Borland C ++安裝程序,該安裝程序是16位應用程序。您將必須在另一個環境上運行它,或者使用Winevdm嘗試運氣。
可以在project目錄中找到Borland Makefile。可以通過執行來完成構建:
cd project
make.exe < options > <options>可以是:
-DDOS32用於32位DPMI應用程序(仍然可以在64位窗口上使用)。-DWIN32用於32位本機Win32應用程序(對於TVDEMO不可能,依賴於farcoreleft()和其他古物)。-DDEBUG構建應用程序和庫的調試版本。-DTVDEBUG將應用程序與庫的調試版本鏈接。-DOVERLAY , -DALIGNMENT={2,4} , -DEXCEPTION , -DNO_STREAMABLE , -DNOTASM我從未使用過的東西,但出現在原始的makefiles中。這將將庫彙編為project旁邊的LIB目錄,並將在其各自的examples/*目錄中編譯演示應用程序的可執行文件。
很抱歉,Root MakeFile假設它是從project目錄執行的。如果要使用不同的設置,您仍然可以直接運行原始的makefile(在source/tvision和examples/* )。
可以使用VCPKG依賴管理器構建和安裝Turbo Vision:
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
./vcpkg integrate install
./vcpkg install tvision Microsoft團隊成員和社區貢獻者保持最新的VCPKG tvision端口。如果您發現它已經過時了,請在VCPKG存儲庫中創建問題或提取請求。
如果您為應用程序選擇CMAKE構建系統,則有兩種主要方法可以鏈接到Turbo Vision:
安裝Turbo視覺並使用find_package導入它。安裝取決於發電機類型:
首先,確定安裝前綴。默認一個將在開箱即用,但通常需要管理特權。在UNIX系統上,您可以使用$HOME/.local 。在Windows上,您可以使用所需的任何自定義路徑,但是在構建應用程序時,必須將其添加到CMAKE_PREFIX_PATH環境變量中。
對於Mono-Config Generator( Unix Makefiles , Ninja ...),您只需要構建和安裝一次:
cmake . -B ./build # '-DCMAKE_INSTALL_PREFIX=...' to override the install prefix.
cmake --build ./build
cmake --install ./build對於多核電生成器( Visual Studio , Ninja Multi-Config ...),您應該構建並安裝所有配置:
cmake . -B ./build # '-DCMAKE_INSTALL_PREFIX=...' to override the install prefix.
cmake --build ./build --config Release
cmake --build ./build --config Debug --target tvision
cmake --build ./build --config RelWithDebInfo --target tvision
cmake --build ./build --config MinSizeRel --target tvision
cmake --install ./build --config Release
cmake --install ./build --config Debug --component library
cmake --install ./build --config RelWithDebInfo --component library
cmake --install ./build --config MinSizeRel --component library然後,在您的應用程序的CMakeLists.txt中,您可以這樣導入它:
find_package (tvision CONFIG)
target_link_libraries (my_application tvision::tvision)在您的存儲庫中的子模塊中具有渦輪視覺,並使用add_subdirectory導入它:
add_subdirectory (tvision) # Assuming Turbo Vision is in the 'tvision' directory.
target_link_libraries (my_application tvision)無論哪種情況, <tvision/tv.h>都將在您的應用程序的包含路徑中可用,並且您的應用程序將自動與必要的庫(ncurses,gpm ...)鏈接。
tvedit應用程序中嘗試一下。~/到$HOME 。stdin / stdout / stderr的重定向不會干擾端子I /O。有一些環境變量影響所有渦輪視覺應用的行為:
TVISION_MAX_FPS :最大刷新率,默認值60 。這可以有助於保持終端模擬器的平滑度,並具有拋光字符的不高效處理。此選項的特殊值為0 ,以禁用刷新率限制, -1在每次呼叫THardwareInfo::screenWrite (在調試時有用)中實際上繪製到終端。
TVISION_CODEPAGE :Turbo Vision內部使用的字符集將延長的ASCII轉換為Unicode。目前有效值為437和850 ,默認值為437 ,儘管添加更多的精力所需的精力。
win32-input-mode (在WSL中可用)。TIOCLINUX )和鼠標(通過GPM)。stderr是TTY時,將其寫入的消息將其重定向到緩衝區,以防止它們弄亂顯示屏,並在退出或暫停應用程序時最終將其打印到控制台上。stderr將失敗。如果您希望保留所有stderr ,只需將其從命令行中重定向到2> 。還考慮了以下環境變量:
TERM :Ncurses使用它來確定終端功能。它是由終端模擬器自動設置的。
COLORTERM :設置為truecolor或24bit ,Turbo Vision將假設末端模擬器支持24位顏色。它是由支持它的終端模擬器自動設置的。
ESCDELAY :收到ESC密鑰按下默認值10後要等待的毫秒數。如果在此延遲期間按下另一個鍵,它將被解釋為Alt+鍵組合。當終端不支持ALT密鑰時,使用較大的值很有用。
TVISION_USE_STDIO :當不空時,終端I / O將通過stdin / stdout執行,以便可以從外殼重定向。默認情況下,Turbo Vision執行終端I/O到/dev/tty ,允許用戶在不影響應用程序穩定性的情況下重定向stdin , stdout和stderr 。
例如,以下將out.txt空:
tvdemo | tee out.txt雖然以下將把應用程序打印的所有逃生序列和文本轉移到out.txt中:
TVISION_USE_STDIO=1 tvdemo | tee out.txt使用Borland C ++編譯時,以下內容不可用:
wchar_t變體。注意: Turbo Vision將UTF-8文本直接寫入Windows控制台。如果將控制台設置為遺留模式並使用位圖字體,則將無法正確顯示Unicode字符(照片)。為了避免這種情況,Turbo Vision檢測到了這種情況,並試圖將控制台字體更改為Consolas或Lucida Console 。
以下是Borland發布Turbo Vision或以前的開源端口(Sigala,Set)中沒有的新功能:
TInputLine s不再滾動文本顯示在焦點/untocus上,從而允許相關文本保持可見。TFileViewer ( tvdemo )和TEditor ( tvedit )中的LF線路結尾。 TEditor保留了在文件保存上結尾的行,但是默認情況下,所有新創建的文件都使用CRLF。TEditor :右鍵單擊上下文菜單。TEditor :使用中間鼠標按鈕拖放滾動。TEditor , TInputLine :用kbAltBack , kbCtrlBack和kbCtrlDel刪除整個單詞。TEditor :主鍵在線路的開始和縮進文本的開始之間切換。TEditor :支持32位或64位構建的文件大於64 KIB的支持。tvdemo :事件查看器applet對活動調試有用。tvdemo :更改背景模式的選項。 屏幕寫入是緩衝的,通常會在活動事件循環的每次迭代中發送到終端一次(另請參見TVISION_MAX_FPS )。如果您需要在繁忙的循環期間更新屏幕,則可以使用TScreen::flushScreen() 。
TDrawBuffer不再是固定長度的數組,其方法阻止了過去的陣列訪問。因此,包含與sizeof(TDrawBuffer)/sizeof(ushort)比較的舊代碼不再有效;此類檢查應刪除。
TApplication現在提供dosShell() , cascade()和tile() ,默認情況下處理cmDosShell , cmCascade和cmTile 。可以通過覆蓋getTileRect()和writeShellMsg()來自定義這些功能。這與Pascal版本中的行為相同。
鼠標車輪支撐:新的鼠標事件evMouseWheel 。車輪方向在新的field event.mouse.wheel中指定,其可能的值是mwUp , mwDown , mwLeft或mwRight 。
中間鼠標按鈕支持:新鼠標按鈕標誌mbMiddleButton 。
evMouseUp事件中的buttons字段不再為空。現在,它指示發布了哪個按鈕。
三人單擊支持:新的鼠標事件標誌meTripleClick 。
TRect方法現在move , grow , intersect和Union現在返回TRect&而不是void ,以便可以束縛它們。
TOutlineViewer現在允許根節點具有兄弟姐妹。
新功能ushort popupMenu(TPoint where, TMenuItem &aMenu, TGroup *receiver=0) ,在桌面上產生了TMenuPopup 。請參閱source/tvision/popupmnu.cpp 。
新的虛擬方法TMenuItem& TEditor::initContextMenu(TPoint p)確定TEditor中右鍵單擊上下文菜單的條目。
fexpand現在可以採用第二個參數relativeTo 。
新類TStringView ,受std::string_view的啟發。
TStringView 。 TStringView與std::string_view , std::string和const char * (甚至nullptr )兼容。由std::span啟發的新類TSpan<T> 。
新類TDrawSurface和TSurfaceView ,請參見<tvision/surface.h> 。
現在,當第一次構造TApplication而不是main之前,系統集成子系統( THardwareInfo , TScreen , TEventQueue ...)現在被初始化。他們仍然在從main出口時被摧毀。
新方法TVMemMgr::reallocateDiscardable()可在allocateDiscardable且freeDiscardable情況下使用。
新方法TView::textEvent()允許以有效的方式接收文本,請參閱剪貼板交互。
新類TClipboard ,請參閱剪貼板交互。
Unicode支持,請參閱Unicode。
真正的顏色支持,請參閱擴展顏色。
新方法static void TEventQueue::waitForEvents(int timeoutMs)它可能會阻止timeoutMs毫秒等待輸入事件。負timeoutMs可以使用負時限制。如果阻止它,它具有沖洗屏幕更新的副作用(通過TScreen::flushScreen() )。它由TProgram::getEvent()帶有static int TProgram::eventTimeoutMs (默認為20 )作為參數調用,以便事件循環不會變成繁忙的循環消耗100%的CPU。
新方法static void TEventQueue::wakeUp() ,如果在TEventQueue::waitForEvents()上阻止了事件循環恢復執行。此方法是線程安全的,因為它的目的是從輔助線程中解開事件循環。
新方法void TView::getEvent(TEvent &, int timeoutMs) ,它允許等待用戶提供超時的事件(而不是TProgram::eventTimeoutMs )。
現在可以在TInputLine中指定最大文本寬度或最大字符計數。這是通過TInputLine的構造器ushort limitMode中的一個新參數來完成的,該參數控制第二個構造函數uint limit的方式。 ilXXXX常數定義了limitMode的可能值:
ilMaxBytes (默認值):文本可以limit長字節,包括null終結器。ilMaxWidth :文本可以向上limit列寬。ilMaxChars :文本可以包含以limit非組合字符或繪畫。允許在運行時獲取Turbo Vision常數名稱的新功能(例如evCommand , kbShiftIns等):
void printKeyCode (ostream &, ushort keyCode);
void printControlKeyState (ostream &, ushort controlKeyState);
void printEventCode (ostream &, ushort eventCode);
void printMouseButtonState (ostream &, ushort buttonState);
void printMouseWheelState (ostream &, ushort wheelState);
void printMouseEventFlags (ostream &, ushort eventFlags);可以通過指定密鑰代碼和密鑰修飾符的掩碼來定義新的密鑰組合(例如Shift+Alt+Up )的新類TKey :
auto kbShiftAltUp = TKey(kbUp, kbShift | kbAltShift);
assert (kbCtrlA == TKey( ' A ' , kbCtrlShift));
assert (TKey(kbCtrlTab, kbShift) == TKey(kbTab, kbShift | kbCtrlShift));
// Create menu hotkeys.
new TMenuItem( " ~R~estart " , cmRestart, TKey(kbDel, kbCtrlShift | kbAltShift), hcNoContext, " Ctrl-Alt-Del " )
// Examine KeyDown events:
if (event.keyDown == TKey(kbEnter, kbShift))
doStuff ();允許使用定時事件的新方法:
TTimerId TView::setTimer ( uint timeoutMs, int periodMs = - 1 );
void TView::killTimer (TTimerId id); setTimer啟動了一個計時器,該計時器將首次在timeoutMs毫秒內,然後是每個periodMs毫秒。
如果periodMs為負,則計時器僅次數一次,並且會自動清理。否則,它將定期定時出現,直到調用killTimer為止。
當計時器乘以時,發出了帶有命令cmTimerExpired的evBroadcast事件,並將message.infoPtr設置為已過期計時器的TTimerId 。
超時事件是在TProgram::idle()中生成的。因此,只有在沒有鍵盤或鼠標事件的情況下才能處理它們。
您會在這裡找到一些屏幕截圖。隨時添加自己的!
如果您知道源代碼尚未丟失並且可以從中受益的任何Turbo Vision應用程序,請告訴我。
如果您的應用程序基於此項目,並且您希望它顯示在以下列表中,請告訴我。
Turbo Vision API已擴展,以允許接收Unicode輸入和顯示Unicode文本。受支持的編碼為UTF-8,原因有很多:
char * )兼容,因此它不需要對現有代碼進行侵入性修改。請注意,當使用Borland C ++構建時,Turbo Vision不支持Unicode。但是,這不會影響Turbo Vision應用程序的編寫方式,因為API擴展旨在允許編碼不可能的代碼。
從關鍵新聞事件中獲取文本的傳統方式如下:
// 'ev' is a TEvent, and 'ev.what' equals 'evKeyDown'.
switch (ev.keyDown.keyCode) {
// Key shortcuts are usually checked first.
// ...
default : {
// The character is encoded in the current codepage
// (CP437 by default).
char c = ev. keyDown . charScan . charCode ;
// ...
}
}一些處理文本輸入的現有渦輪視覺類仍取決於這種方法,但沒有改變。單字節字符在當前代碼epage中表示時,繼續在ev.keyDown.charScan.charCode中可用。
Unicode支持在ev.keyDown中的兩個新字段(這是一個struct KeyDownEvent ):
char text[4] ,其中可能包含終端中讀取的內容:通常是UTF-8序列,但可能是任何類型的原始數據。uchar textLength是text中可用數據的字節數,從0到4。請注意, text字符串未終止。您可以使用getText()方法從KeyDownEvent中獲得TStringView 。
因此,可以通過以下方式從TEvent中檢索一個unicode字符:
switch (ev.keyDown.keyCode) {
// ...
default : {
std::string_view sv = ev. keyDown . getText ();
processText (sv);
}
}讓我們從另一個角度看一下。如果用戶鍵入ñ ,則會使用以下keyDown結構生成TEvent :
KeyDownEvent {
union {
. keyCode = 0xA4 ,
. charScan = CharScanType {
. charCode = 164 ( ' ñ ' ), // In CP437
. scanCode = 0
}
},
. controlKeyState = 0x200 (kbInsState),
. text = { ' xC3 ' , ' xB1 ' }, // In UTF-8
. textLength = 2
}但是,如果它們輸入€則會發生以下內容:
KeyDownEvent {
union {
. keyCode = 0x0 (kbNoKey), // '€' not part of CP437
. charScan = CharScanType {
. charCode = 0 ,
. scanCode = 0
}
},
. controlKeyState = 0x200 (kbInsState),
. text = { ' xE2 ' , ' x82 ' , ' xAC ' }, // In UTF-8
. textLength = 3
}如果按下密鑰快捷方式,則text為空:
KeyDownEvent {
union {
. keyCode = 0xB (kbCtrlK),
. charScan = CharScanType {
. charCode = 11 ( ' ♂ ' ),
. scanCode = 0
}
},
. controlKeyState = 0x20C (kbCtrlShift | kbInsState),
. text = {},
. textLength = 0
}因此,簡而言之:在沒有Unicode輸入的情況下設計的視圖將繼續像以前一樣繼續工作,並且想要成為Unicode-Away的視圖將沒有問題。
Turbo Vision的原始設計使用16位代表一個屏幕單元格-8位為角色,而BIOS顏色屬性為8位。
<tvision/scrncell.h>定義了一種新的TScreenCell類型,該類型能夠持有有限數量的UTF-8 CodePoint,此外還可以持有擴展屬性(BOLD,UNEWERLINE,ITALIC ...)。但是,您不應將文本直接寫入TScreenCell ,而是使用Unicode-ware API函數。
作為參數為參數的任何一個參數的角色,與顯示文本顯示的任何渦輪視覺API函數都解釋如下:
0x00至0xFF中的不可打印字符被解釋為Active codepage中的字符。例如,如果使用CP437,則顯示為0x7F ≡ ⌂和0xF0 。例外,始終將0x00顯示為常規空間。這些字符都是一列寬。例如,字符串"╔[xFE]╗"可以顯示為╔[■]╗ 。這意味著可以將盒裝字符與UTF-8混合在一起,這對於向後兼容很有用。但是,如果您依靠此行為,則可能會得到意外的結果:例如, "xC4xBF"是有效的UTF-8序列,顯示為Ŀ而不是─┐ 。
Unicode支持的問題之一是存在雙寬字符和結合字符。這與Turbo Vision的原始假設相抵觸,即屏幕是每個字符佔據的單元格的網格。然而,這些案例以以下方式處理:
雙寬字符可以在屏幕上的任何地方繪製,如果部分與其他字符重疊,則不會發生任何不好的情況。
零寬的字符覆蓋了上一個字符。例如,序列में由單寬字符म和組合字符े和ं組成。在這種情況下,將三個Unicode編碼點擬合到同一單元格中。
ZERO WIDTH JOINER ( U+200D )始終省略,因為它使事情變得太多了。例如,它可以將字符串像"??"這樣的字符串轉動。 (4列寬)進入"??" (2列寬)。並非所有末端模擬器都尊重ZWJ,因此,為了產生可預測的結果,Turbo Vision將兩者都印刷"??"和"??"作為?? 。
只要您的末端模擬器尊重特徵寬度,就不會像通過wcwidth測量的特徵寬度那樣出現明顯的圖形故障。
這是Turbo文本編輯器中此類字符的示例: 
寫在屏幕上的通常方法是使用TDrawBuffer 。添加了一些方法,其他方法改變了其含義:
void TDrawBuffer::moveChar ( ushort indent, char c, TColorAttr attr, ushort count);
void TDrawBuffer::putChar ( ushort indent, char c); c始終將其解釋為Active Codepage中的字符。
ushort TDrawBuffer::moveStr ( ushort indent, TStringView str, TColorAttr attr);
ushort TDrawBuffer::moveCStr ( ushort indent, TStringView str, TAttrPair attrs);根據先前暴露的規則來解釋str 。
ushort TDrawBuffer::moveStr ( ushort indent, TStringView str, TColorAttr attr, ushort maxWidth, ushort strOffset = 0 ); // New
ushort TDrawBuffer::moveCStr ( ushort indent, TStringView str, TColorAttr attr, ushort maxWidth, ushort strOffset = 0 ); // New根據以前暴露的規則來解釋str ,但:
maxWidth指定應從str複製的最大文本量,以文本寬度測量(不在字節中)。strOffset指定str中的初始位置在哪裡複製,以文本寬度測量(不在字節中)。這對於水平滾動很有用。如果strOffset指向雙寬字符的中間,則將復制一個空間,而不是雙寬字符的右半部分,因為不可能執行此類操作。返回值是實際填充文本的緩衝區中的單元格數(與復製文本的寬度相同)。
void TDrawBuffer::moveBuf ( ushort indent, const void *source, TColorAttr attr, ushort count);此功能的名稱具有誤導性。即使在其原始實現中, source也被視為字符串。因此,它等效於moveStr(indent, TStringView((const char*) source, count), attr) 。
還有其他有用的Unicode感知功能:
int cstrlen (TStringView s);根據上述規則返回s顯示的長度,丟棄~ 。
int strwidth (TStringView s); // New返回s的顯示長度。
在Borland C ++上,這些方法假定單字節編碼,並且所有字符均為一列。這使得可以編寫在沒有單個#ifdef兩個平台上使用的編碼 - agnostic draw()和handleEvent()方法。
上面的功能是使用TText名稱空間(另一個API擴展名)的功能實現的。如果您想手動填充TScreenCell對象,則必須直接使用它們。舉個例子,以下是一些TText函數。您可以在<tvision/ttext.h>中找到所有使用完整描述的所有內容。
size_t TText::next (TStringView text);
size_t TText::prev (TStringView text, size_t index);
void TText::drawChar (TSpan<TScreenCell> cells, char c);
size_t TText::drawStr (TSpan<TScreenCell> cells, size_t indent, TStringView text, int textIndent);
bool TText::drawOne (TSpan<TScreenCell> cells, size_t &i, TStringView text, size_t &j);為了將TScreenCell緩衝區繪製到視圖中,可以使用以下方法:
void TView::writeBuf ( short x, short y, short w, short h, const TScreenCell *b); // New
void TView::writeLine ( short x, short y, short w, short h, const TScreenCell *b); // New盡可能簡單。讓我們修改hello.cpp如下:
TMenuBar * THelloApp::initMenuBar ( TRect r )
{
r. b . y = r. a . y + 1 ;
return new TMenuBar ( r,
* new TSubMenu ( " ~Ñ~ello " , kbAltH ) +
* new TMenuItem ( "階~毎~料入報最... " , GreetThemCmd, kbAltG ) +
* new TMenuItem ( "五劫~の~擦り切れ" , cmYes, kbNoKey, hcNoContext ) +
* new TMenuItem ( " העברית ~א~ינטרנט " , cmNo, kbNoKey, hcNoContext ) +
newLine () +
* new TMenuItem ( " E~x~it " , cmQuit, cmQuit, hcNoContext, " Alt-X " )
);
}
TStatusLine * THelloApp::initStatusLine ( TRect r )
{
r. a . y = r. b . y - 1 ;
return new TStatusLine ( r,
* new TStatusDef ( 0 , 0xFFFF ) +
* new TStatusItem ( " ~Alt-Ç~ Exit " , kbAltX, cmQuit ) +
* new TStatusItem ( 0 , kbF10, cmMenu )
);
}這是它的樣子:

draw()方法以下是TFileViewer::draw()的舊實現的摘錄( tvdemo應用程序的一部分),該實現無法正確繪製Unicode文本:
if (delta.y + i < fileLines-> getCount ()) {
char s[maxLineLength+ 1 ];
p = ( char *)(fileLines-> at (delta. y +i));
if (p == 0 || strlen (p) < delta. x )
s[ 0 ] = EOS;
else
strnzcpy (s, p+delta. x , maxLineLength+ 1 );
b. moveStr ( 0 , s, c);
}
writeBuf ( 0 , i, size.x, 1 , b );它所做的就是fileLines中的字符串的一部分移至b ,這是TDrawBuffer 。 delta是代表文本視圖中滾動偏移的TPoint , i是正在處理的可見線的索引。 c是文本顏色。存在一些問題:
TDrawBuffer::moveStr(ushort, const char *, TColorAttr)採用一個無終端的字符串。為了通過當前行的子字符串,將副本放入s中,以緩衝區超支的風險。該線不適合s的情況不被處理,因此大多數maxLineLenght字符都將被複製。此外, maxLineLength接近位置的多型字符可能不完全複製,並顯示為垃圾。delta.x是第一個可見列。借助多鍵編碼的文本,此類列不再是在字符串中的位置delta.x開始。以下是上面代碼的校正版本,可正確處理Unicode:
if (delta.y + i < fileLines-> getCount ()) {
p = ( char *)(fileLines-> at (delta. y +i));
if (p)
b. moveStr ( 0 , p, c, size. x , delta. x );
}
writeBuf ( 0 , i, size.x, 1 , b );這裡使用的moveStr的過載是TDrawBuffer::moveStr(ushort indent, TStringView str, TColorAttr attr, ushort width, ushort begin) 。此功能不僅提供了Unicode支持,而且還可以幫助我們編寫清潔器代碼並克服以前存在的一些限制:
maxLineLength字節。moveStr從列delta.x開始打印字符串。我們甚至不需要擔心多少個字節對應於delta.x列。moveStr在大多數size.x上複製文本列,而無需我們關心多少個字節,而不會處理邊緣案例。該代碼以編碼方式編寫,無論是否考慮多重字符,都將起作用。TView::writeBuf已經在照顧不超越它的寫作。然而,有趣的是,不僅要限制功能,而且很容易出現錯誤,這很有趣。 支持創建Unicode-Aware視圖的支持已經到位,原始渦輪視覺庫中的大多數視圖已適用於處理Unicode。
以下視圖可以正確顯示Unicode文本。其中一些還進行水平滾動或單詞包裝;所有這些都應該很好。
TStaticText ( 7b15d45d )。 TFrame ( 81066ee5 )。 TStatusLine ( 477b3ae9 )。 THistoryViewer ( 81066ee5 )。 THelpViewer ( 81066ee5 , 20f331e3 8c7dac2a 。 TListViewer ( 81066ee5 )。 TMenuBox ( 81066ee5 )。 TTerminal ( ee821b69 )。 TOutlineViewer ( 6cc8cd38 )。 TFileViewer (來自tvdemo應用程序)( 068bbf7a )。 TFilePane (來自tvdir應用程序)( 9bcd897c )。另外,以下視圖可以處理Unicode文本或用戶輸入:
TInputLine ( 81066ee5 , cb489d42 )。 TEditor ( 702114dc )。默認情況下,實例處於UTF-8模式。您可以通過按Ctrl+P切換回單字節模式。這僅改變了顯示文檔的顯示方式和用戶輸入的編碼;它不會改變文檔。該類用於tvedit應用程序;您可以在那裡測試。此列表中不需要的視圖可能不需要任何更正,或者我只是忘記修復它們。如果您注意到任何無法正常工作的問題,請提交問題。
不支持Unicode的用例(不是詳盡的列表):
TMenuBox , TStatusLine , TButton ...)。 最初,Turbo Vision與系統剪貼板沒有任何集成,因為MS-DOS上沒有這樣的東西。
它確實提供了通過TEditor::clipboard靜態成員使用TEditor實例作為內部剪貼板的可能性。但是, TEditor是唯一能夠與此剪貼板進行交互的類。例如,不可能將其與TInputLine一起使用。
現在,Turbo Vision應用最有可能通過終端模擬器在圖形環境中運行。在這種情況下,希望以與常規GUI應用程序相同的方式與系統剪貼板進行交互。
為了解決此問題,已經添加了一個新的TClipboard ,允許訪問系統剪貼板。如果無法訪問系統剪貼板,則將使用內部剪貼板。
在Windows(包括WSL)和MACOS上,剪貼板集成在框外支持。
在MacOS以外的UNIX系統上,有必要安裝一些外部依賴關係。請參閱運行時要求。
對於遠程運行的應用程序(例如,通過SSH),在以下情況下支持剪貼板集成:
ssh -X )。allowWindowOps選項。此外,始終可以使用終端模擬器自己的粘貼命令(通常Ctrl+Shift+V或Cmd+V )粘貼文本。
要使用TClipboard類,請在包含<tvision/tv.h>之前定義宏Uses_TClipboard 。
static void TClipboard::setText (TStringView text);將系統剪貼板的內容設置為text 。如果無法訪問系統剪貼板,則使用內部剪貼板。
static void TClipboard::requestText ();請求系統剪貼板的內容異步,稍後將以常規evKeyDown事件的形式收到。如果無法訪問系統剪貼板,則使用內部剪貼板。
Turbo Vision應用程序可能會接受糊狀事件,原因有兩個不同:
TClipboard::requestText() 。在這兩種情況下,應用程序都將以常規evKeyDown事件的形式接收剪貼板內容。這些事件將在keyDown.controlKeyState中具有kbPaste標誌,以便可以將它們與常規密鑰媒體區分開。
因此,如果您的視圖可以處理用戶輸入,則默認情況下將處理粘貼事件。但是,如果用戶粘貼了5000個字符,則該應用程序將表現得好像用戶按下鍵盤5000次一樣。這涉及繪製視圖,完成事件循環,更新屏幕...,例如,如果您的視圖是文本編輯組件,則遠非最佳。
為了處理這種情況,添加了另一個功能:
bool TView::textEvent (TEvent &event, TSpan< char > dest, size_t &length); textEvent()試圖從連續的evKeyDown事件中讀取文本,並將其存儲在用戶提供的緩衝區dest中。當沒有更多事件可用或找到非文本事件時,它將返回false ,在這種情況下,此事件將用putEvent()保存,因此可以在事件循環的下一次迭代中處理。最後,它稱為clearEvent(event) 。
確切的字節讀數數量存儲在輸出參數length中,該長度永遠不會大於dest.size() 。
這是如何使用它的示例:
// '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'...
}
}標準視圖TEditor和TInputLine對cmCut , cmCopy和cmPaste命令做出反應。但是,必須首先設置您的應用程序才能使用這些命令。例如:
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和TInputLine自動啟用並禁用這些命令。例如,如果將TEditor或TInputLine集中在集中,則將啟用cmPaste命令。如果選擇文本,則還將啟用cmCut和cmCopy命令。如果沒有將TEditor或TInputLine s聚焦,則將禁用這些命令。
Turbo Vision API已被擴展到允許的16種顏色。
可以使用以下任何格式指定顏色:
xterm-256color色板指數(8位)。儘管Turbo Vision應用可能會在終端模擬器中運行,但API對顯示器沒有任何假設。也就是說,處理終端模擬器的複雜性是對程序員隱藏的,並由Turbo Vision本身管理。
例如:顏色支持在終端之間有所不同。如果程序員使用終端模擬器不支持的顏色格式,則Turbo Vision將其量化為終端顯示的內容。以下圖像代表了24位RGB圖片的量化為256、16和8調色板:
| 24位顏色(原始) | 256種顏色 |
|---|---|
![]() | ![]() |
| 16種顏色 | 8種顏色(大膽如明亮) |
|---|---|
![]() | ![]() |
擴展的顏色支持基本上歸結為以下內容:
uchar中的BIOS顏色屬性。 ushort用於表示屬性對。使用Borland C ++時仍然是這種情況。TColorAttr ,它取代了uchar 。它指定了前景和背景顏色和样式。顏色可以以不同的格式(BIOS顏色屬性,24位RGB ...)指定。樣式是典型的(粗體,斜體,下劃線...)。還有TAttrPair ,取代了ushort 。TDrawBuffer的方法,該方法曾經使用uchar或ushort參數指定顏色屬性,現在使用TColorAttr或TAttrPair 。uchar數組的TPalette現在包含一個TColorAttr的數組。 TView::mapColor方法還返回TColorAttr而不是uchar 。TView::mapColor已成為虛擬的,以便可以繞過調色板系統,而無需重寫任何draw方法。TColorAttr和TAttrPair初始化並投入uchar和ushort ,以使舊版代碼仍在封閉式內部編譯,而無需任何功能。以下是針對開發人員的更詳細的解釋。
首先,我們將解釋程序員需要知道的數據類型,以便利用擴展的顏色支持。要訪問它們,您可能必須在包含<tvision/tv.h>之前定義宏Uses_TColorAttr 。
本節中描述的所有類型都是微不足道的。這意味著它們可以memset “ d and memcpy ” d。但是,當沒有初始化器的聲明時,這些類型的變量是不可分化的,就像原始類型一樣。因此,請確保在初始化它們之前不要操縱它們。
定義了幾種代表不同顏色格式的類型。這些類型存在的原因是允許使用類型系統區分顏色格式。他們中的一些人也有公共場所,可以更輕鬆地操縱單個位。
TColorBIOS代表BIOS顏色。它允許單獨訪問r , g , b和bright位,並可以隱式地施放到uint8_t中。
內存佈局是:
b )。g )。r )。bright )。 TColorBIOS使用的示例:
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.在終端模擬器中,BIOS顏色映射到基本的16個ANSI顏色。
TColorRGB表示24位RGB中的顏色。它允許單獨訪問r , g和b位字段,並可以隱式地施放到uint32_t中。
內存佈局是:
b )。g )。r )。 TColorRGB使用的示例:
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代表xterm-256color調色板的索引。它可以從uint8_t施放。
TColorDesired TColorDesired代表程序員打算在屏幕上顯示的顏色,該顏色在任何受支持的顏色類型中編碼。
可以通過以下方式初始化TColorDesired :
作為BIOS顏色:帶有char文字或TColorBIOS對象:
TColorDesired bios1 = ' xF ' ;
TColorDesired bios2 = TColorBIOS( 0xF );作為RGB顏色:帶有int字面或TColorRGB對象:
TColorDesired rgb1 = 0xFF7700 ; // 0xRRGGBB.
TColorDesired rgb2 = TColorRGB( 0xFF , 0x77 , 0x00 ); // {R, G, B}.
TColorDesired rgb3 = TColorRGB( 0xFF7700 ); // 0xRRGGBB.作為Xterm調色板索引:帶有TColorXTerm對象。
作為終端默認顏色:通過零限制:
TColorDesired def1 {};
// Or with 'memset':
TColorDesired def2;
memset (&def2, 0 , sizeof (def2)); TColorDesired具有查詢包含的顏色的方法,但通常不需要使用它們。有關更多信息,請參見<tvision/colors.h>中的結構定義。
Trivia:這個名字的靈感來自Scintilla的ColourDesired 。
TColorAttr TColorAttr描述了屏幕單元格的顏色屬性。如果您打算在視圖中更改顏色,這是您最有可能與之互動的類型。
TColorAttr由:
前景顏色,類型為TColorDesired 。
背景顏色,類型為TColorDesired 。
一個樣式的bitmask,其中包含以下標誌的組合:
slBold 。slItalic 。slUnderline 。slBlink 。slReverse 。slStrike 。這些標誌基於通過ANSI逃生代碼選擇的基本顯示屬性。終端模擬器之間的結果可能會有所不同。 slReverse可能是最不可靠的:更喜歡使用TColorAttr reverseAttribute(TColorAttr attr)自由功能而不是設置此標誌。
創建TColorAttr的最直接方法是通過TColorAttr(TColorDesired fg, TColorDesired bg, ushort style=0)和TColorAttr(int bios)構造函數:
// 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 ;可以使用以下免費功能訪問TColorAttr的字段:
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是一對TColorAttr ,某些API函數用於一次傳遞兩個屬性。
您可以使用TAttrPair(const TColorAttrs &lo, const TColorAttrs &hi)構造器初始化TAttrPair :
TColorAttr cNormal = { 0x234983 , 0x267232 };
TColorAttr cHigh = { 0x309283 , 0x127844 };
TAttrPair attrs = {cNormal, cHigh};
TDrawBuffer b;
b.moveCStr( 0 , " Normal text, ~Highlighted text~ " , attrs);可以使用[0]和[1]子指數訪問該屬性:
TColorAttr lo = { 0x892343 , 0x271274 };
TColorAttr hi = ' x93 ' ;
TAttrPair attrs = {lo, hi};
assert (lo == attrs[ 0 ]);
assert (hi == attrs[ 1 ]);TView的外觀視圖通常是通過TDrawBuffer繪製的。大多數TDrawBuffer成員功能按參數採用顏色屬性。例如:
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);但是,帶有渦輪視覺的視圖通常將其顏色信息存儲在調色板中。可以通過以下成員功能查詢視圖的調色板:
TColorAttr TView::mapColor (uchar index);
TAttrPair TView::getColor ( ushort indices); mapColor在View的調色板中查找了一個顏色屬性,並在調色板中給出了索引。請記住,每個視圖類的調色板索引都可以在Turbo Vision標頭中找到。例如, <tvision/views.h>說明了有關TScrollBar的以下內容:
/* ---------------------------------------------------------------------- */
/* class TScrollBar */
/* */
/* Palette layout */
/* 1 = Page areas */
/* 2 = Arrows */
/* 3 = Indicator */
/* ---------------------------------------------------------------------- */ getColor是一個輔助功能,允許一次查詢兩個單元格屬性。 indices參數中的每個字節都包含調色板中的索引。 TAttrPair結果包含兩個單元格屬性。
例如,可以在TMenuBar的draw方法中找到以下內容:
TAttrPair cNormal = getColor( 0x0301 );
TAttrPair cSelect = getColor( 0x0604 );這等同於此:
TAttrPair cNormal = { mapColor ( 1 ), mapColor ( 3 )};
TAttrPair cSelect = { mapColor ( 4 ), mapColor ( 6 )};作為API擴展, mapColor方法已成為virtual 。這使得可以使用自定義解決方案覆蓋Turbo Vision的分層調色板系統,而無需重寫draw()方法。
因此,通常,有三種方法可以在視圖中使用擴展顏色:
mapColor方法返回擴展顏色屬性: // 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;
}
}通過將擴展的顏色屬性直接提供給TDrawBuffer方法,如果未使用調色板系統。例如:
// 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);
/* ... */
}通過修改調色板。有兩種方法可以做到這一點:
TColorAttr 。例如: void updateAppPalette ()
{
TPalette &pal = TProgram::application-> getPalete ();
pal[ 1 ] = { 0x762892 , 0x828712 }; // TBackground.
pal[ 2 ] = { 0x874832 , 0x249838 , slBold}; // TMenuView normal text.
pal[ 3 ] = {{}, {}, slItalic | slUnderline}; // TMenuView disabled text.
/* ... */
} static const TColorAttr cpMyApp[] =
{
{ 0x762892 , 0x828712 }, // TBackground.
{ 0x874832 , 0x249838 , slBold}, // TMenuView normal text.
{{}, {}, slItalic | slUnderline}, // TMenuView disabled text.
/* ... */
};
// The 'TMyApp' class inherits from 'TApplication' and overrides 'TView::getPalette'.
TPalette & TMyApp::getPalette () const
{
static TPalette palette (cpMyApp);
return palette;
}TScreen::screenMode公開了有關顯示器的顏色支持的一些信息:
(TScreen::screenMode & 0xFF) == TDisplay::smMono ,則顯示為單色(僅與DOS相關)。(TScreen::screenMode & 0xFF) == TDisplay::smBW80 ,則顯示為grayscale(僅在DOS中相關)。(TScreen::screenMode & 0xFF) == TDisplay::smCO80 ,顯示器至少支持16種顏色。TScreen::screenMode & TDisplay::smColor256 ,則顯示至少256種顏色。TScreen::screenMode & TDisplay::smColorHigh ,則顯示屏將支持更多顏色(例如24位顏色)。在這種情況下,也設置了TDisplay::smColor256 。 前面定義的類型表示為Borland C ++開發時也很重要的概念:
| 概念 | Borland C ++的佈局 | 現代平台的佈局 |
|---|---|---|
| 顏色屬性 | uchar 。 BIOS顏色屬性。 | struct TColorAttr 。 |
| 顏色 | 一個4位編號。 | struct TColorDesired 。 |
| 屬性對 | ushort 。每個字節中的屬性。 | struct TAttrPair 。 |
該項目的關鍵原則之一是,應在Borland C ++和現代平台中以相同的方式使用API,也就是說,無需#ifdef s。另一個原則是,舊版代碼應在開箱即用,並將其調整為新功能應盡可能少地增加複雜性。
向後兼容是通過以下方式完成的:
在Borland C ++中, TColorAttr和TAttrPair分別與uchar和ushort分別typedef 。
在現代平台中, TColorAttr和TAttrPair可以分別代替uchar和ushort ,因為它們能夠將任何適合其中的價值保存,並且可以將其隱含地施加到它們中。
用uchar初始化的TColorAttr表示BIOS顏色屬性。轉換回uchar時,發生以下情況:
fg和bg是BIOS顏色,並且style被清除,則結果uchar表示TColorAttr中包含的相同的BIOS顏色屬性(如上所述)。TColorAttr / TAttrPair代替uchar / ushort ,如果它們打算支持擴展的顏色屬性。考慮到它由兩個TColorAttr組成, TAttrPair和ushort也是如此。
渦輪視覺本身中向後兼容的用例是調色板系統的核心TPalette類。在其原始設計中,它使用單個數據類型( uchar )表示不同的內容:數組長度,調色板索引或顏色屬性。
新設計只需用TColorAttr取代uchar 。這意味著使用TPalette的方式沒有變化,但是TPalette現在可以存儲擴展的顏色屬性。
TColorDialog尚未進行重塑,因此不能在運行時選擇擴展的顏色屬性。
以下代碼模式在draw視圖方法中很常見:
void TMyView::draw ()
{
ushort cFrame, cTitle;
if (state & sfDragging)
{
cFrame = 0x0505 ;
cTitle = 0x0005 ;
}
else
{
cFrame = 0x0503 ;
cTitle = 0x0004 ;
}
cFrame = getColor (cFrame);
cTitle = getColor (cTitle);
/* ... */
}在這種情況下, ushort既將Ushort用作一對調色板索引,又用作一對顏色屬性。 getColor現在返回TAttrPair ,因此,即使該編譯開箱即用,在隱性轉換為ushort中也會丟失擴展屬性。
上面的代碼仍然像最初一樣起作用。只有非BIO顏色屬性不會產生預期的結果。由於TAttrPair和ushort之間的兼容性,以下足以支持擴展顏色屬性:
- ushort cFrame, cTitle;
+ TAttrPair cFrame, cTitle;沒有什麼可以阻止您對調色板索引和顏色屬性使用不同的變量,這是實際要做的。向後兼容的目的是能夠在不更改程序邏輯的情況下支持新功能的能力,也就是說,最小化了增加代碼複雜性或引入錯誤的風險。