CoolFace
Datasetpublic

dlxjj/imradv3

sourceHugging Faceupdated 8mo agoView on Hugging Face
0likes263downloads
TextEditor.cpp3537 linesDownload Raw Back to ImGuiColorTextEdit
1#include <algorithm>2#include <chrono>3#include <string>4#include <regex>5#include <cmath>6#include <vector>7#include <ranges>  // 需支持 C++208 9#include "TextEditor.h"10 11#define IMGUI_DEFINE_MATH_OPERATORS12#include "imgui.h" // for imGui::GetCurrentWindow()13 14 15//#include <opencv2/core.hpp>16//#include <opencv2/highgui.hpp>17//#include <opencv2/imgcodecs.hpp>18 19#include <opencv2/opencv.hpp>20#include <GLFW/glfw3.h> 21#include "../imrad.h"22 23//#pragma comment(lib, "E:\\huggingface\\ColorTextEditorV2\\build\\bin\\opencv_core4120.lib")24 25#include "../nlohmann_json/json.hpp"26#include "../global.h"27 28 29 30using json = nlohmann::json;31 32//#include "../MessageQueue.h"33 34// TODO35// - multiline comments vs single-line: latter is blocking start of a ML36 37template<class InputIt1, class InputIt2, class BinaryPredicate>38bool equals(InputIt1 first1, InputIt1 last1,39	InputIt2 first2, InputIt2 last2, BinaryPredicate p)40{41	for (; first1 != last1 && first2 != last2; ++first1, ++first2)42	{43		if (!p(*first1, *first2))44			return false;45	}46	return first1 == last1 && first2 == last2;47}48 49TextEditor::TextEditor(ImTextureID texture)50	: mLineSpacing(1.0f)51	, mUndoIndex(0)52	, mTabSize(4)53	, mOverwrite(false)54	, mReadOnly(false)55	, mWithinRender(false)56	, mScrollToCursor(false)57	, mScrollToTop(false)58	, mTextChanged(false)59	, mColorizerEnabled(true)60	, mTextStart(20.0f)61	, mLeftMargin(10)62	, mCursorPositionChanged(false)63	, mColorRangeMin(0)64	, mColorRangeMax(0)65	, mSelectionMode(SelectionMode::Normal)66	, mCheckComments(true)67	, mLastClick(-1.0f)68	, mHandleKeyboardInputs(true)69	, mHandleMouseInputs(true)70	, mIgnoreImGuiChild(false)71	, mShowWhitespaces(true)72	, mStartTime(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count())73{74	SetPalette(GetDarkPalette());75	SetLanguageDefinition(LanguageDefinition::HLSL());76	mLines.push_back(Line());77	mRect.SetTexture(texture);78}79 80TextEditor::~TextEditor()81{82}83 84void TextEditor::SetLanguageDefinition(const LanguageDefinition & aLanguageDef)85{86	mLanguageDefinition = aLanguageDef;87	mRegexList.clear();88 89	for (auto& r : mLanguageDefinition.mTokenRegexStrings)90		mRegexList.push_back(std::make_pair(std::regex(r.first, std::regex_constants::optimize), r.second));91 92	Colorize();93}94 95void TextEditor::SetPalette(const Palette & aValue)96{97	mPaletteBase = aValue;98}99 100std::string TextEditor::GetText(const Coordinates & aStart, const Coordinates & aEnd) const101{102	std::string result;103 104	auto lstart = aStart.mLine;105	auto lend = aEnd.mLine;106	auto istart = GetCharacterIndex(aStart);107	auto iend = GetCharacterIndex(aEnd);108	size_t s = 0;109 110	for (size_t i = lstart; i < lend; i++)111		s += mLines[i].size();112 113	result.reserve(s + s / 8);114 115	while (istart < iend || lstart < lend)116	{117		if (lstart >= (int)mLines.size())118			break;119 120		auto& line = mLines[lstart];121		if (istart < (int)line.size())122		{123			result += line[istart].mChar;124			istart++;125		}126		else127		{128			istart = 0;129			++lstart;130			result += '\n';131		}132	}133 134	return result;135}136 137TextEditor::Coordinates TextEditor::GetActualCursorCoordinates() const138{139	return SanitizeCoordinates(mState.mCursorPosition);140}141 142TextEditor::Coordinates TextEditor::SanitizeCoordinates(const Coordinates & aValue) const143{144	auto line = aValue.mLine;145	auto column = aValue.mColumn;146	if (line >= (int)mLines.size())147	{148		if (mLines.empty())149		{150			line = 0;151			column = 0;152		}153		else154		{155			line = (int)mLines.size() - 1;156			column = GetLineMaxColumn(line);157		}158		return Coordinates(line, column);159	}160	else161	{162		column = mLines.empty() ? 0 : std::min(column, GetLineMaxColumn(line));163		return Coordinates(line, column);164	}165}166 167// https://en.wikipedia.org/wiki/UTF-8168// We assume that the char is a standalone character (<128) or a leading byte of an UTF-8 code sequence (non-10xxxxxx code)169static int UTF8CharLength(TextEditor::Char c)170{171	if ((c & 0xFE) == 0xFC)172		return 6;173	if ((c & 0xFC) == 0xF8)174		return 5;175	if ((c & 0xF8) == 0xF0)176		return 4;177	else if ((c & 0xF0) == 0xE0)178		return 3;179	else if ((c & 0xE0) == 0xC0)180		return 2;181	return 1;182}183 184// "Borrowed" from ImGui source185static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c)186{187	if (c < 0x80)188	{189		buf[0] = (char)c;190		return 1;191	}192	if (c < 0x800)193	{194		if (buf_size < 2) return 0;195		buf[0] = (char)(0xc0 + (c >> 6));196		buf[1] = (char)(0x80 + (c & 0x3f));197		return 2;198	}199	if (c >= 0xdc00 && c < 0xe000)200	{201		return 0;202	}203	if (c >= 0xd800 && c < 0xdc00)204	{205		if (buf_size < 4) return 0;206		buf[0] = (char)(0xf0 + (c >> 18));207		buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));208		buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));209		buf[3] = (char)(0x80 + ((c) & 0x3f));210		return 4;211	}212	//else if (c < 0x10000)213	{214		if (buf_size < 3) return 0;215		buf[0] = (char)(0xe0 + (c >> 12));216		buf[1] = (char)(0x80 + ((c >> 6) & 0x3f));217		buf[2] = (char)(0x80 + ((c) & 0x3f));218		return 3;219	}220}221 222void TextEditor::Advance(Coordinates & aCoordinates) const223{224	if (aCoordinates.mLine < (int)mLines.size())225	{226		auto& line = mLines[aCoordinates.mLine];227		auto cindex = GetCharacterIndex(aCoordinates);228 229		if (cindex + 1 < (int)line.size())230		{231			auto delta = UTF8CharLength(line[cindex].mChar);232			cindex = std::min(cindex + delta, (int)line.size() - 1);233		}234		else235		{236			++aCoordinates.mLine;237			cindex = 0;238		}239		aCoordinates.mColumn = GetCharacterColumn(aCoordinates.mLine, cindex);240	}241}242 243void TextEditor::DeleteRange(const Coordinates & aStart, const Coordinates & aEnd)244{245	assert(aEnd >= aStart);246	assert(!mReadOnly);247 248	//printf("D(%d.%d)-(%d.%d)\n", aStart.mLine, aStart.mColumn, aEnd.mLine, aEnd.mColumn);249 250	if (aEnd == aStart)251		return;252 253	auto start = GetCharacterIndex(aStart);254	auto end = GetCharacterIndex(aEnd);255 256	if (aStart.mLine == aEnd.mLine)257	{258		auto& line = mLines[aStart.mLine];259		auto n = GetLineMaxColumn(aStart.mLine);260		if (aEnd.mColumn >= n)261			line.erase(line.begin() + start, line.end());262		else263			line.erase(line.begin() + start, line.begin() + end);264	}265	else266	{267		auto& firstLine = mLines[aStart.mLine];268		auto& lastLine = mLines[aEnd.mLine];269 270		firstLine.erase(firstLine.begin() + start, firstLine.end());271		lastLine.erase(lastLine.begin(), lastLine.begin() + end);272 273		if (aStart.mLine < aEnd.mLine)274			firstLine.insert(firstLine.end(), lastLine.begin(), lastLine.end());275 276		if (aStart.mLine < aEnd.mLine)277			RemoveLine(aStart.mLine + 1, aEnd.mLine + 1);278	}279 280	mTextChanged = true;281}282 283int TextEditor::InsertTextAt(Coordinates& /* inout */ aWhere, const char * aValue)284{285	assert(!mReadOnly);286 287	int cindex = GetCharacterIndex(aWhere);288	int totalLines = 0;289	while (*aValue != '\0')290	{291		assert(!mLines.empty());292 293		if (*aValue == '\r')294		{295			// skip296			++aValue;297		}298		else if (*aValue == '\n')299		{300			if (cindex < (int)mLines[aWhere.mLine].size())301			{302				auto& newLine = InsertLine(aWhere.mLine + 1);303				auto& line = mLines[aWhere.mLine];304				newLine.insert(newLine.begin(), line.begin() + cindex, line.end());305				line.erase(line.begin() + cindex, line.end());306			}307			else308			{309				InsertLine(aWhere.mLine + 1);310			}311			++aWhere.mLine;312			aWhere.mColumn = 0;313			cindex = 0;314			++totalLines;315			++aValue;316		}317		else318		{319			auto& line = mLines[aWhere.mLine];320			auto d = UTF8CharLength(*aValue);321			while (d-- > 0 && *aValue != '\0')322				line.insert(line.begin() + cindex++, Glyph(*aValue++, PaletteIndex::Default));323			++aWhere.mColumn;324		}325 326		mTextChanged = true;327	}328 329	return totalLines;330}331 332void TextEditor::AddUndo(UndoRecord& aValue)333{334	assert(!mReadOnly);335	//printf("AddUndo: (@%d.%d) +\'%s' [%d.%d .. %d.%d], -\'%s', [%d.%d .. %d.%d] (@%d.%d)\n",336	//	aValue.mBefore.mCursorPosition.mLine, aValue.mBefore.mCursorPosition.mColumn,337	//	aValue.mAdded.c_str(), aValue.mAddedStart.mLine, aValue.mAddedStart.mColumn, aValue.mAddedEnd.mLine, aValue.mAddedEnd.mColumn,338	//	aValue.mRemoved.c_str(), aValue.mRemovedStart.mLine, aValue.mRemovedStart.mColumn, aValue.mRemovedEnd.mLine, aValue.mRemovedEnd.mColumn,339	//	aValue.mAfter.mCursorPosition.mLine, aValue.mAfter.mCursorPosition.mColumn340	//	);341 342	mUndoBuffer.resize((size_t)(mUndoIndex + 1));343	mUndoBuffer.back() = aValue;344	++mUndoIndex;345}346 347TextEditor::Coordinates TextEditor::ScreenPosToCoordinates(const ImVec2& aPosition) const348{349	ImVec2 origin = ImGui::GetCursorScreenPos();350	ImVec2 local(aPosition.x - origin.x, aPosition.y - origin.y);351 352	int lineNo = std::max(0, (int)floor(local.y / mCharAdvance.y));353	lineNo = std::min(lineNo, (int)mLines.size() - 1);354 355	int columnCoord = 0;356 357	if (lineNo >= 0 && lineNo < (int)mLines.size())358	{359		auto& line = mLines.at(lineNo);360 361		int columnIndex = 0;362		float columnX = 0.0f;363 364		while ((size_t)columnIndex < line.size())365		{366			float columnWidth = 0.0f;367 368			if (line[columnIndex].mChar == '\t')369			{370				float spaceSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, " ").x;371				float oldX = columnX;372				float newColumnX = (1.0f + std::floor((1.0f + columnX) / (float(mTabSize) * spaceSize))) * (float(mTabSize) * spaceSize);373				columnWidth = newColumnX - oldX;374				if (mTextStart + columnX + columnWidth * 0.5f > local.x)375					break;376				columnX = newColumnX;377				columnCoord = (columnCoord / mTabSize) * mTabSize + mTabSize;378				columnIndex++;379			}380			else381			{382				char buf[7];383				auto d = UTF8CharLength(line[columnIndex].mChar);384				int i = 0;385				while (i < 6 && d-- > 0)386					buf[i++] = line[columnIndex++].mChar;387				buf[i] = '\0';388				columnWidth = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf).x;389				if (mTextStart + columnX + columnWidth * 0.5f > local.x)390					break;391				columnX += columnWidth;392				columnCoord++;393			}394		}395	}396 397	return SanitizeCoordinates(Coordinates(lineNo, columnCoord));398}399 400TextEditor::Coordinates TextEditor::FindWordStart(const Coordinates & aFrom) const401{402	Coordinates at = aFrom;403	if (at.mLine >= (int)mLines.size())404		return at;405 406	auto& line = mLines[at.mLine];407	auto cindex = GetCharacterIndex(at);408 409	if (cindex >= (int)line.size())410		return at;411 412	while (cindex > 0 && isspace(line[cindex].mChar))413		--cindex;414 415	auto cstart = (PaletteIndex)line[cindex].mColorIndex;416	while (cindex > 0)417	{418		auto c = line[cindex].mChar;419		if ((c & 0xC0) != 0x80)	// not UTF code sequence 10xxxxxx420		{421			if (c <= 32 && isspace(c))422			{423				cindex++;424				break;425			}426			if (cstart != (PaletteIndex)line[size_t(cindex - 1)].mColorIndex)427				break;428		}429		--cindex;430	}431	return Coordinates(at.mLine, GetCharacterColumn(at.mLine, cindex));432}433 434TextEditor::Coordinates TextEditor::FindWordEnd(const Coordinates & aFrom) const435{436	Coordinates at = aFrom;437	if (at.mLine >= (int)mLines.size())438		return at;439 440	auto& line = mLines[at.mLine];441	auto cindex = GetCharacterIndex(at);442 443	if (cindex >= (int)line.size())444		return at;445 446	bool prevspace = (bool)isspace(line[cindex].mChar);447	auto cstart = (PaletteIndex)line[cindex].mColorIndex;448	while (cindex < (int)line.size())449	{450		auto c = line[cindex].mChar;451		auto d = UTF8CharLength(c);452		if (cstart != (PaletteIndex)line[cindex].mColorIndex)453			break;454 455		if (prevspace != !!isspace(c))456		{457			if (isspace(c))458				while (cindex < (int)line.size() && isspace(line[cindex].mChar))459					++cindex;460			break;461		}462		cindex += d;463	}464	return Coordinates(aFrom.mLine, GetCharacterColumn(aFrom.mLine, cindex));465}466 467TextEditor::Coordinates TextEditor::FindNextWord(const Coordinates & aFrom) const468{469	Coordinates at = aFrom;470	if (at.mLine >= (int)mLines.size())471		return at;472 473	// skip to the next non-word character474	auto cindex = GetCharacterIndex(aFrom);475	bool isword = false;476	bool skip = false;477	if (cindex < (int)mLines[at.mLine].size())478	{479		auto& line = mLines[at.mLine];480		isword = isalnum(line[cindex].mChar);481		skip = isword;482	}483 484	while (!isword || skip)485	{486		if (at.mLine >= mLines.size())487		{488			auto l = std::max(0, (int) mLines.size() - 1);489			return Coordinates(l, GetLineMaxColumn(l));490		}491 492		auto& line = mLines[at.mLine];493		if (cindex < (int)line.size())494		{495			isword = isalnum(line[cindex].mChar);496 497			if (isword && !skip)498				return Coordinates(at.mLine, GetCharacterColumn(at.mLine, cindex));499 500			if (!isword)501				skip = false;502 503			cindex++;504		}505		else506		{507			cindex = 0;508			++at.mLine;509			skip = false;510			isword = false;511		}512	}513 514	return at;515}516 517int TextEditor::GetCharacterIndex(const Coordinates& aCoordinates) const518{519	if (aCoordinates.mLine >= mLines.size())520		return -1;521	auto& line = mLines[aCoordinates.mLine];522	int c = 0;523	int i = 0;524	for (; i < line.size() && c < aCoordinates.mColumn;)525	{526		if (line[i].mChar == '\t')527			c = (c / mTabSize) * mTabSize + mTabSize;528		else529			++c;530		i += UTF8CharLength(line[i].mChar);531	}532	return i;533}534 535int TextEditor::GetCharacterColumn(int aLine, int aIndex) const536{537	if (aLine >= mLines.size())538		return 0;539	auto& line = mLines[aLine];540	int col = 0;541	int i = 0;542	while (i < aIndex && i < (int)line.size())543	{544		auto c = line[i].mChar;545		i += UTF8CharLength(c);546		if (c == '\t')547			col = (col / mTabSize) * mTabSize + mTabSize;548		else549			col++;550	}551	return col;552}553 554int TextEditor::GetLineCharacterCount(int aLine) const555{556	if (aLine >= mLines.size())557		return 0;558	auto& line = mLines[aLine];559	int c = 0;560	for (unsigned i = 0; i < line.size(); c++)561		i += UTF8CharLength(line[i].mChar);562	return c;563}564 565int TextEditor::GetLineMaxColumn(int aLine) const566{567	if (aLine >= mLines.size())568		return 0;569	auto& line = mLines[aLine];570	int col = 0;571	for (unsigned i = 0; i < line.size(); )572	{573		auto c = line[i].mChar;574		if (c == '\t')575			col = (col / mTabSize) * mTabSize + mTabSize;576		else577			col++;578		i += UTF8CharLength(c);579	}580	return col;581}582 583bool TextEditor::IsOnWordBoundary(const Coordinates & aAt) const584{585	if (aAt.mLine >= (int)mLines.size() || aAt.mColumn == 0)586		return true;587 588	auto& line = mLines[aAt.mLine];589	auto cindex = GetCharacterIndex(aAt);590	if (cindex >= (int)line.size())591		return true;592 593	if (mColorizerEnabled)594		return line[cindex].mColorIndex != line[size_t(cindex - 1)].mColorIndex;595 596	return isspace(line[cindex].mChar) != isspace(line[cindex - 1].mChar);597}598 599void TextEditor::RemoveLine(int aStart, int aEnd)600{601	assert(!mReadOnly);602	assert(aEnd >= aStart);603	assert(mLines.size() > (size_t)(aEnd - aStart));604 605	ErrorMarkers etmp;606	for (auto& i : mErrorMarkers)607	{608		ErrorMarkers::value_type e(i.first >= aStart ? i.first - 1 : i.first, i.second);609		if (e.first >= aStart && e.first <= aEnd)610			continue;611		etmp.insert(e);612	}613	mErrorMarkers = std::move(etmp);614 615	Breakpoints btmp;616	for (auto i : mBreakpoints)617	{618		if (i >= aStart && i <= aEnd)619			continue;620		btmp.insert(i >= aStart ? i - 1 : i);621	}622	mBreakpoints = std::move(btmp);623 624	mLines.erase(mLines.begin() + aStart, mLines.begin() + aEnd);625	assert(!mLines.empty());626 627	mTextChanged = true;628}629 630void TextEditor::RemoveLine(int aIndex)631{632	assert(!mReadOnly);633	assert(mLines.size() > 1);634 635	ErrorMarkers etmp;636	for (auto& i : mErrorMarkers)637	{638		ErrorMarkers::value_type e(i.first > aIndex ? i.first - 1 : i.first, i.second);639		if (e.first - 1 == aIndex)640			continue;641		etmp.insert(e);642	}643	mErrorMarkers = std::move(etmp);644 645	Breakpoints btmp;646	for (auto i : mBreakpoints)647	{648		if (i == aIndex)649			continue;650		btmp.insert(i >= aIndex ? i - 1 : i);651	}652	mBreakpoints = std::move(btmp);653 654	mLines.erase(mLines.begin() + aIndex);655	assert(!mLines.empty());656 657	mTextChanged = true;658}659 660TextEditor::Line& TextEditor::InsertLine(int aIndex)661{662	assert(!mReadOnly);663 664	auto& result = *mLines.insert(mLines.begin() + aIndex, Line());665 666	ErrorMarkers etmp;667	for (auto& i : mErrorMarkers)668		etmp.insert(ErrorMarkers::value_type(i.first >= aIndex ? i.first + 1 : i.first, i.second));669	mErrorMarkers = std::move(etmp);670 671	Breakpoints btmp;672	for (auto i : mBreakpoints)673		btmp.insert(i >= aIndex ? i + 1 : i);674	mBreakpoints = std::move(btmp);675 676	return result;677}678 679std::string TextEditor::GetWordUnderCursor() const680{681	auto c = GetCursorPosition();682	return GetWordAt(c);683}684 685std::string TextEditor::GetWordAt(const Coordinates & aCoords) const686{687	auto start = FindWordStart(aCoords);688	auto end = FindWordEnd(aCoords);689 690	std::string r;691 692	auto istart = GetCharacterIndex(start);693	auto iend = GetCharacterIndex(end);694 695	for (auto it = istart; it < iend; ++it)696		r.push_back(mLines[aCoords.mLine][it].mChar);697 698	return r;699}700 701TextEditor::Glyph TextEditor::GetCharactersAroundCursor() const702{703	// 获取当前光标位置704	auto cursor = GetActualCursorCoordinates();705 706	// 检查行是否有效707	if (cursor.mLine >= 0 && cursor.mLine < (int)mLines.size())708	{709		const auto& line = mLines[cursor.mLine];710 711		char buf[7];712		int columnIndex = GetCharacterIndex({ cursor.mLine, cursor.mColumn });713		if (columnIndex >= 0 && columnIndex < line.size()) {714			Glyph gh = line[columnIndex];715			auto d = UTF8CharLength(gh.mChar);716			int i = 0;717			while (i < 6 && d-- > 0)718				buf[i++] = line[columnIndex++].mChar;719			buf[i] = '\0';720 721			sprintf(gh.mUtf8Char, "%s", buf);722 723			int utf8CharWidth = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf).x;724 725			return gh;726		}727 728		//return Glyph();729 730		//// 获取光标前后的字符731		//char prevChar = (cursor.mColumn > 0) ? line[GetCharacterIndex({ cursor.mLine, cursor.mColumn - 1 })].mChar : '\0';732		//char nextChar = (cursor.mColumn < GetLineMaxColumn(cursor.mLine)) ? line[GetCharacterIndex({ cursor.mLine, cursor.mColumn })].mChar : '\0';733 734		//return { prevChar, nextChar };735	}736 737	return Glyph();738 739}740 741//void TextEditor::SetMessageQueue(MessageQueue* queue) { mMessageQueue = queue; }742 743ImU32 TextEditor::GetGlyphColor(const Glyph & aGlyph) const744{745	if (!mColorizerEnabled)746		return mPalette[(int)PaletteIndex::Default];747	if (aGlyph.mComment)748		return mPalette[(int)PaletteIndex::Comment];749	if (aGlyph.mMultiLineComment)750		return mPalette[(int)PaletteIndex::MultiLineComment];751	auto const color = mPalette[(int)aGlyph.mColorIndex];752	if (aGlyph.mPreprocessor)753	{754		const auto ppcolor = mPalette[(int)PaletteIndex::Preprocessor];755		const int c0 = ((ppcolor & 0xff) + (color & 0xff)) / 2;756		const int c1 = (((ppcolor >> 8) & 0xff) + ((color >> 8) & 0xff)) / 2;757		const int c2 = (((ppcolor >> 16) & 0xff) + ((color >> 16) & 0xff)) / 2;758		const int c3 = (((ppcolor >> 24) & 0xff) + ((color >> 24) & 0xff)) / 2;759		return ImU32(c0 | (c1 << 8) | (c2 << 16) | (c3 << 24));760	}761	return color;762}763 764void TextEditor::HandleKeyboardInputs()765{766	ImGuiIO& io = ImGui::GetIO();767	auto shift = io.KeyShift;768	auto ctrl = io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl;769	auto alt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt;770 771	if (ImGui::IsWindowFocused())772	{773		if (ImGui::IsWindowHovered())774			ImGui::SetMouseCursor(ImGuiMouseCursor_TextInput);775 776		io.WantCaptureKeyboard = true;777		io.WantTextInput = true;778 779		if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Z))780			Undo();781		else if (!IsReadOnly() && !ctrl && !shift && alt && ImGui::IsKeyPressed(ImGuiKey_Backspace))782			Undo();783		else if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Y))784			Redo();785		else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGuiKey_UpArrow))786			MoveUp(1, shift);787		else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGuiKey_DownArrow))788			MoveDown(1, shift);789		else if (!alt && ImGui::IsKeyPressed(ImGuiKey_LeftArrow))790			MoveLeft(1, shift, ctrl);791		else if (!alt && ImGui::IsKeyPressed(ImGuiKey_RightArrow))792			MoveRight(1, shift, ctrl);793		else if (!alt && ImGui::IsKeyPressed(ImGuiKey_PageUp))794			MoveUp(GetPageSize() - 4, shift);795		else if (!alt && ImGui::IsKeyPressed(ImGuiKey_PageDown))796			MoveDown(GetPageSize() - 4, shift);797		else if (!alt && ctrl && ImGui::IsKeyPressed(ImGuiKey_Home))798			MoveTop(shift);799		else if (ctrl && !alt && ImGui::IsKeyPressed(ImGuiKey_End))800			MoveBottom(shift);801		else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGuiKey_Home))802			MoveHome(shift);803		else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGuiKey_End))804			MoveEnd(shift);805		else if (!IsReadOnly() && !ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Delete))806			Delete();807		else if (!IsReadOnly() && !ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Backspace))808			Backspace();809		else if (!ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Insert))810			mOverwrite ^= true;811		else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Insert))812			Copy();813		else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_C))814			Copy();815		else if (!IsReadOnly() && !ctrl && shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Insert))816			Paste();817		else if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_V))818			Paste();819		else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_X))820			Cut();821		else if (!ctrl && shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Delete))822			Cut();823		else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_A))824			SelectAll();825		else if (!IsReadOnly() && !ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Enter))826			EnterCharacter('\n', false);827		else if (!IsReadOnly() && !ctrl && !alt && ImGui::IsKeyPressed(ImGuiKey_Tab))828			EnterCharacter('\t', shift);829 830		if (!IsReadOnly() && !io.InputQueueCharacters.empty())831		{832			for (int i = 0; i < io.InputQueueCharacters.Size; i++)833			{834				auto c = io.InputQueueCharacters[i];835				if (c != 0 && (c == '\n' || c >= 32))836					EnterCharacter(c, shift);837			}838			io.InputQueueCharacters.resize(0);839		}840	}841}842 843void TextEditor::HandleMouseInputs()844{845	ImGuiIO& io = ImGui::GetIO();846	auto shift = io.KeyShift;847	auto ctrl = io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl;848	auto alt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt;849 850	if (ImGui::IsWindowHovered())851	{852		if (!shift && !alt)853		{854			auto click = ImGui::IsMouseClicked(0);855			auto doubleClick = ImGui::IsMouseDoubleClicked(0);856			auto t = ImGui::GetTime();857			auto tripleClick = click && !doubleClick && (mLastClick != -1.0f && (t - mLastClick) < io.MouseDoubleClickTime);858 859			/*860			Left mouse button triple click861			*/862 863			if (tripleClick)864			{865				if (!ctrl)866				{867					mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos());868					mSelectionMode = SelectionMode::Line;869					SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode);870				}871 872				mLastClick = -1.0f;873			}874 875			/*876			Left mouse button double click877			*/878 879			else if (doubleClick)880			{881				if (!ctrl)882				{883					mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos());884					if (mSelectionMode == SelectionMode::Line)885						mSelectionMode = SelectionMode::Normal;886					else887						mSelectionMode = SelectionMode::Word;888					SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode);889				}890 891				mLastClick = (float)ImGui::GetTime();892			}893 894			/*895			Left mouse button click896			*/897			else if (click)898			{899				mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos());900				if (ctrl)901					mSelectionMode = SelectionMode::Word;902				else903					mSelectionMode = SelectionMode::Normal;904				SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode);905 906				if (!mRect.isHovered) {907					Glyph gh = GetCharactersAroundCursor(); // gh.mUtf8Char 就是当前光标后面的一个完整 utf8 字符908					if (click ) { // && mMessageQueue909 910						const char* m5 = gh.mImageMD5;911						if (m5 && *m5) {912							auto [pth_img, pth_json] = get_img_json_pth(std::string(m5));913 914							std::ifstream f(pth_json);915							if (!f.is_open()) {916							}917							json jsn = json::parse(f);918 919							int width = jsn["width"];920							int height = jsn["height"];921 922							json prism_wordsInfo = jsn["prism_wordsInfo"];923 924							std::string word_line;925							json line_pos;926							927							bool found = false;928 929							for (const auto& elem : prism_wordsInfo) {930								word_line = elem["word"]; // 图片上的一行文字931								line_pos = elem["pos"];   // 这一行在图片上的坐标932								json charInfo = elem["charInfo"];933								934								for (const auto& elem2 : charInfo) {935									const std::string& utf8_char = elem2["word"];936									int x = elem2["x"];937									int y = elem2["y"];938									int w = elem2["w"];939									int h = elem2["h"];940 941									if (gh.x == x && gh.y == y && gh.w == w && gh.h == h) {942										found = true;943										break;944									}945 946									//for (auto chr : utf8_char)  // utf8_char 可能是好几个字节,emplace_back 进去的很可能只是一部分947									//{948									//	//if (chr == '\r')949									//	//{950									//	//	// 理论上不应该有951									//	//}952									//	//else if (chr == '\n') {953									//	//	// 理论上不应该有954									//	//}955									//	//else956									//	//{957									//	//	mLines.back().emplace_back(Glyph(chr, PaletteIndex::Default, x, y, w, h));958									//	//}959 960									//	//mLines.back().emplace_back(Glyph(chr, PaletteIndex::Default, x, y, w, h, md5.c_str()));961									//}962								}963 964								if (found) {965									break;966								}967							}968 969							if (found) {970								// 左上 右上 右下 左下971								int x = line_pos[0]["x"];972								int y = line_pos[0]["y"];973 974								int x_min = std::min(line_pos[0]["x"], line_pos[3]["x"]);975								int x_max = std::max(line_pos[1]["x"], line_pos[2]["x"]);976 977								int y_min = std::min(line_pos[0]["y"], line_pos[1]["y"]);978								int y_max = std::max(line_pos[2]["y"], line_pos[3]["y"]);979 980								int w = x_max - x_min;981								int h = y_max - y_min;982								983								auto b64_str = readFileToString(pth_img.string());984								auto b64_buf = base64_decode(b64_str);985 986								//cv::Mat srcImage = cv::imread("E:\\er.jpeg", cv::IMREAD_COLOR_BGR);987								auto srcImage = cv::imdecode(b64_buf, -1);988 989								switch (srcImage.channels()) {990									case 1: // 单通道灰度图 → 扩展为 RGBA991										cv::cvtColor(srcImage, srcImage, cv::COLOR_GRAY2RGBA);992										break;993									case 3: // 三通道BGR → 转换为 RGBA994										cv::cvtColor(srcImage, srcImage, cv::COLOR_BGR2RGBA);995										break;996									case 4: // 四通道直接使用(需确认OpenCV为BGRA)997										//convertedImage = srcImage.clone();998										break;999								}1000 1001								cv::rectangle(srcImage, cv::Point(gh.x, gh.y), cv::Point(gh.x + gh.w, gh.y + gh.h), cv::Scalar(255, 0, 0, 255), 2);1002									// 框出选中字符1003 1004								// 剪裁图片1005								cv::Mat cut = srcImage(cv::Rect(x, y, w, h)).clone(); // cv::Rect(0, 0, img_orgin.cols, 500)1006 1007								ImRad::Texture tex2;1008								unsigned char* image_data2 = cut.data;1009								tex2.w = cut.cols;1010								tex2.h = cut.rows;1011 1012								GLuint image_texture;1013								glGenTextures(1, &image_texture);1014								tex2.id = (ImTextureID)(intptr_t)image_texture;1015								glBindTexture(GL_TEXTURE_2D, image_texture);1016 1017 1018								int minFilter = GL_LINEAR;1019								int magFilter = GL_LINEAR;1020								int wrapS = GL_CLAMP_TO_EDGE; // This is required on WebGL for non power-of-two textures1021								int wrapT = GL_CLAMP_TO_EDGE; // Same1022 1023								// Setup filtering parameters for display1024								glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);1025								glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);1026								glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapS);1027								glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapT);1028 1029								// Upload pixels into texture1030								glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);1031 1032								glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex2.w, tex2.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data2);1033								//stbi_image_free(image_data2);1034 1035								mRect.size = ImVec2((float)tex2.w, (float)tex2.h);1036								mRect.SetTexture(tex2.id);1037 1038								//cv::imshow("原图", cut);1039								//cv::waitKey(0);1040 1041							}1042 1043 1044 1045						}1046 1047 1048 1049						//Message msg;1050						//msg.type = MessageType::EditorClick;1051						//auto mousePos = ImGui::GetMousePos();1052						//msg.x = (int)mousePos.x;1053						//msg.y = (int)mousePos.y;1054 1055						// 启动异步线程加载图片1056						//std::thread([this, msg]() {1057 1058						//	// 发消息通知弹窗显示图片1059						//	this->mMessageQueue->push(msg);1060 1061						//}).detach();1062 1063					}1064				}1065 1066				mLastClick = (float)ImGui::GetTime();1067			}1068			// Mouse left button dragging (=> update selection)1069			else if (ImGui::IsMouseDragging(0) && ImGui::IsMouseDown(0))1070			{1071				// 只有在非图片拖动状态下才进行文本选择1072				if (!mRect.isHovered)1073				{1074					io.WantCaptureMouse = true;1075					mState.mCursorPosition = mInteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos());1076					SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode);1077				}1078			}1079		}1080	}1081}1082 1083void TextEditor::Render()1084{1085	/* Compute mCharAdvance regarding to scaled font size (Ctrl + mouse wheel)*/1086	const float fontSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, "#", nullptr, nullptr).x;1087	mCharAdvance = ImVec2(fontSize, ImGui::GetTextLineHeightWithSpacing() * mLineSpacing);1088 1089	/* Update palette with the current alpha from style */1090	for (int i = 0; i < (int)PaletteIndex::Max; ++i)1091	{1092		auto color = ImGui::ColorConvertU32ToFloat4(mPaletteBase[i]);1093		color.w *= ImGui::GetStyle().Alpha;1094		mPalette[i] = ImGui::ColorConvertFloat4ToU32(color);1095	}1096 1097	assert(mLineBuffer.empty());1098 1099	auto contentSize = ImGui::GetWindowContentRegionMax();1100	auto drawList = ImGui::GetWindowDrawList();1101	float longest(mTextStart);1102 1103	if (mScrollToTop)1104	{1105		mScrollToTop = false;1106		ImGui::SetScrollY(0.f);1107	}1108 1109	ImVec2 cursorScreenPos = ImGui::GetCursorScreenPos();1110	auto scrollX = ImGui::GetScrollX();1111	auto scrollY = ImGui::GetScrollY();1112 1113	auto lineNo = (int)floor(scrollY / mCharAdvance.y);1114	auto globalLineMax = (int)mLines.size();1115	auto lineMax = std::max(0, std::min((int)mLines.size() - 1, lineNo + (int)floor((scrollY + contentSize.y) / mCharAdvance.y)));1116 1117	// Deduce mTextStart by evaluating mLines size (global lineMax) plus two spaces as text width1118	char buf[16];1119	snprintf(buf, 16, " %d ", globalLineMax);1120	mTextStart = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf, nullptr, nullptr).x + mLeftMargin;1121 1122	if (!mLines.empty())1123	{1124		float spaceSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, " ", nullptr, nullptr).x;1125 1126		while (lineNo <= lineMax)1127		{1128			ImVec2 lineStartScreenPos = ImVec2(cursorScreenPos.x, cursorScreenPos.y + lineNo * mCharAdvance.y);1129			ImVec2 textScreenPos = ImVec2(lineStartScreenPos.x + mTextStart, lineStartScreenPos.y);1130 1131			auto& line = mLines[lineNo];1132			longest = std::max(mTextStart + TextDistanceToLineStart(Coordinates(lineNo, GetLineMaxColumn(lineNo))), longest);1133			auto columnNo = 0;1134			Coordinates lineStartCoord(lineNo, 0);1135			Coordinates lineEndCoord(lineNo, GetLineMaxColumn(lineNo));1136 1137			// Draw selection for the current line1138			float sstart = -1.0f;1139			float ssend = -1.0f;1140 1141			assert(mState.mSelectionStart <= mState.mSelectionEnd);1142			if (mState.mSelectionStart <= lineEndCoord)1143				sstart = mState.mSelectionStart > lineStartCoord ? TextDistanceToLineStart(mState.mSelectionStart) : 0.0f;1144			if (mState.mSelectionEnd > lineStartCoord)1145				ssend = TextDistanceToLineStart(mState.mSelectionEnd < lineEndCoord ? mState.mSelectionEnd : lineEndCoord);1146 1147			if (mState.mSelectionEnd.mLine > lineNo)1148				ssend += mCharAdvance.x;1149 1150			if (sstart != -1 && ssend != -1 && sstart < ssend)1151			{1152				ImVec2 vstart(lineStartScreenPos.x + mTextStart + sstart, lineStartScreenPos.y);1153				ImVec2 vend(lineStartScreenPos.x + mTextStart + ssend, lineStartScreenPos.y + mCharAdvance.y);1154				drawList->AddRectFilled(vstart, vend, mPalette[(int)PaletteIndex::Selection]);1155			}1156 1157			// Draw breakpoints1158			auto start = ImVec2(lineStartScreenPos.x + scrollX, lineStartScreenPos.y);1159 1160			if (mBreakpoints.count(lineNo + 1) != 0)1161			{1162				auto end = ImVec2(lineStartScreenPos.x + contentSize.x + 2.0f * scrollX, lineStartScreenPos.y + mCharAdvance.y);1163				drawList->AddRectFilled(start, end, mPalette[(int)PaletteIndex::Breakpoint]);1164			}1165 1166			// Draw error markers1167			auto errorIt = mErrorMarkers.find(lineNo + 1);1168			if (errorIt != mErrorMarkers.end())1169			{1170				auto end = ImVec2(lineStartScreenPos.x + contentSize.x + 2.0f * scrollX, lineStartScreenPos.y + mCharAdvance.y);1171				drawList->AddRectFilled(start, end, mPalette[(int)PaletteIndex::ErrorMarker]);1172 1173				if (ImGui::IsMouseHoveringRect(lineStartScreenPos, end))1174				{1175					ImGui::BeginTooltip();1176					ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.2f, 0.2f, 1.0f));1177					ImGui::Text("Error at line %d:", errorIt->first);1178					ImGui::PopStyleColor();1179					ImGui::Separator();1180					ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 1.0f, 0.2f, 1.0f));1181					ImGui::Text("%s", errorIt->second.c_str());1182					ImGui::PopStyleColor();1183					ImGui::EndTooltip();1184				}1185			}1186 1187			// Draw line number (right aligned)1188			snprintf(buf, 16, "%d  ", lineNo + 1);1189 1190			auto lineNoWidth = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf, nullptr, nullptr).x;1191			drawList->AddText(ImVec2(lineStartScreenPos.x + mTextStart - lineNoWidth, lineStartScreenPos.y), mPalette[(int)PaletteIndex::LineNumber], buf);1192 1193			if (mState.mCursorPosition.mLine == lineNo)1194			{1195				auto focused = ImGui::IsWindowFocused();1196 1197				// Highlight the current line (where the cursor is)1198				if (!HasSelection())1199				{1200					auto end = ImVec2(start.x + contentSize.x + scrollX, start.y + mCharAdvance.y);

Showing the first 1,200 of 3537 lines. Download the file for the rest.