From 27e37c5bf3907d8378611fbafa1fc5afa4c01843 Mon Sep 17 00:00:00 2001 From: vczh Date: Sat, 6 Sep 2025 07:21:59 -0700 Subject: [PATCH] Update release --- Import/GacUI.Windows.cpp | 599 +++++++++++-------- Import/GacUI.Windows.h | 13 +- Import/GacUI.cpp | 289 ++++++--- Import/GacUI.h | 19 +- Import/GacUIReflection.cpp | 2 + Import/Vlpp.cpp | 1 + Tools/Reflection32.bin | Bin 981858 -> 982322 bytes Tools/Reflection64.bin | Bin 981858 -> 982322 bytes Tutorial/GacUI_HelloWorlds/UIRes/Xml.bin.x64 | Bin 25176 -> 25176 bytes Tutorial/GacUI_HelloWorlds/UIRes/Xml.bin.x86 | Bin 25176 -> 25176 bytes 10 files changed, 579 insertions(+), 344 deletions(-) diff --git a/Import/GacUI.Windows.cpp b/Import/GacUI.Windows.cpp index fd0fb0df..a1d1a494 100644 --- a/Import/GacUI.Windows.cpp +++ b/Import/GacUI.Windows.cpp @@ -8773,10 +8773,10 @@ WindowsGDIParagraph void PrepareUniscribeData() { - if(paragraph->BuildUniscribeData(renderTarget->GetDC())) + if (paragraph->BuildUniscribeData(renderTarget->GetDC())) { - vint width=paragraph->lastAvailableWidth==-1?65536:paragraph->lastAvailableWidth; - paragraph->Layout(width, paragraph->paragraphAlignment); + vint width = paragraph->lastAvailableWidth == -1 ? 65536 : paragraph->lastAvailableWidth; + paragraph->Layout(paragraph->lastWrapLine, width, paragraph->paragraphAlignment); } } @@ -8829,18 +8829,13 @@ WindowsGDIParagraph bool GetWrapLine()override { - return paragraph->wrapLine; + return paragraph->lastWrapLine; } void SetWrapLine(bool value)override { - CHECK_ERROR(value, L"vl::presentation::elements_windows_gdi::WindowsGDIParagraph::SetWrapLine(bool)#Non-wrapline not implemented."); - if (paragraph->wrapLine != value) - { - paragraph->wrapLine = value; - paragraph->BuildUniscribeData(renderTarget->GetDC()); - paragraph->Layout(paragraph->lastAvailableWidth, paragraph->paragraphAlignment); - } + paragraph->BuildUniscribeData(renderTarget->GetDC()); + paragraph->Layout(value, paragraph->lastAvailableWidth, paragraph->paragraphAlignment); } vint GetMaxWidth()override @@ -8851,7 +8846,7 @@ WindowsGDIParagraph void SetMaxWidth(vint value)override { paragraph->BuildUniscribeData(renderTarget->GetDC()); - paragraph->Layout(value, paragraph->paragraphAlignment); + paragraph->Layout(paragraph->lastWrapLine, value, paragraph->paragraphAlignment); } Alignment GetParagraphAlignment()override @@ -8862,7 +8857,7 @@ WindowsGDIParagraph void SetParagraphAlignment(Alignment value)override { paragraph->BuildUniscribeData(renderTarget->GetDC()); - paragraph->Layout(paragraph->lastAvailableWidth, value); + paragraph->Layout(paragraph->lastWrapLine, paragraph->lastAvailableWidth, value); } bool SetFont(vint start, vint length, const WString& value)override @@ -8976,7 +8971,7 @@ WindowsGDIParagraph { PrepareUniscribeData(); return Size( - (paragraph->wrapLine ? 0 : paragraph->bounds.Width()), + (paragraph->lastWrapLine ? 0 : paragraph->bounds.Width()), paragraph->bounds.Height()); } @@ -9000,6 +8995,31 @@ WindowsGDIParagraph return true; } + bool IsCaretBoundsFromTextRun(vint caret, bool caretFrontSide) + { + if (!paragraph->IsValidCaret(caret)) return false; + + vint frontLine = 0; + vint backLine = 0; + paragraph->GetLineIndexFromTextPos(caret, frontLine, backLine); + if (frontLine == -1 || backLine == -1) return false; + + vint lineIndex = caretFrontSide ? frontLine : backLine; + lineIndex = lineIndex < 0 ? 0 : lineIndex > paragraph->lines.Count() - 1 ? paragraph->lines.Count() - 1 : lineIndex; + Ptr line = paragraph->lines[caretFrontSide ? frontLine : backLine]; + + vint frontRun = -1; + vint backRun = -1; + paragraph->GetRunIndexFromTextPos(caret, lineIndex, frontRun, backRun); + if (frontRun == -1 || backRun == -1) return false; + + vint runIndex = caretFrontSide ? frontRun : backRun; + runIndex = runIndex < 0 ? 0 : runIndex > line->scriptRuns.Count() - 1 ? line->scriptRuns.Count() - 1 : runIndex; + Ptr run = line->scriptRuns[runIndex]; + + return run.Cast(); + } + void Render(Rect bounds)override { PrepareUniscribeData(); @@ -9010,17 +9030,17 @@ WindowsGDIParagraph paragraph->Render(this, false); paragraphDC = 0; - if(caret!=-1) + if (caret != -1) { - Rect caretBounds=GetCaretBounds(caret, caretFrontSide); - vint x=caretBounds.x1+bounds.x1; - vint y1=caretBounds.y1+bounds.y1; - vint y2=y1+(vint)(caretBounds.Height()*1.5); + Rect caretBounds = GetCaretBounds(caret, caretFrontSide); + vint x = caretBounds.x1 + bounds.x1; + vint y1 = caretBounds.y1 + bounds.y1; + vint y2 = y1 + (vint)(caretBounds.Height() * (IsCaretBoundsFromTextRun(caret, caretFrontSide) ? 1.5 : 1)); - WinDC* dc=renderTarget->GetDC(); + WinDC* dc = renderTarget->GetDC(); dc->SetPen(caretPen); - dc->MoveTo(x-1, y1); - dc->LineTo(x-1, y2); + dc->MoveTo(x - 1, y1); + dc->LineTo(x - 1, y2); dc->MoveTo(x, y1); dc->LineTo(x, y2); } @@ -10838,7 +10858,7 @@ UniscribeTextRun return SumHeight(); } - void UniscribeTextRun::SearchForLineBreak(vint tempStart, vint maxWidth, bool firstRun, vint& charLength, vint& charAdvances) + void UniscribeTextRun::SearchForLineBreak(vint tempStart, bool wrapLine, vint maxWidth, bool firstRun, vint& charLength, vint& charAdvances) { vint width=0; charLength=0; @@ -10847,7 +10867,7 @@ UniscribeTextRun { if(i==length || scriptItem->charLogattrs[i+(startFromLine-scriptItem->startFromLine)].fSoftBreak==TRUE) { - if(width<=maxWidth || (firstRun && charLength==0)) + if(!wrapLine || width<=maxWidth || (firstRun && charLength==0)) { charLength=i-tempStart; charAdvances=width; @@ -11030,9 +11050,9 @@ UniscribeEmbeddedObjectRun return 0; } - void UniscribeEmbeddedObjectRun::SearchForLineBreak(vint tempStart, vint maxWidth, bool firstRun, vint& charLength, vint& charAdvances) + void UniscribeEmbeddedObjectRun::SearchForLineBreak(vint tempStart, bool wrapLine, vint maxWidth, bool firstRun, vint& charLength, vint& charAdvances) { - if (firstRun || properties.size.x <= maxWidth) + if (firstRun || !wrapLine || properties.size.x <= maxWidth) { charLength = length - tempStart; charAdvances = properties.size.x; @@ -11299,17 +11319,17 @@ UniscribeLine return false; } - void UniscribeLine::Layout(vint availableWidth, Alignment alignment, vint top, vint& totalHeight) + void UniscribeLine::Layout(bool wrapLine, vint availableWidth, Alignment alignment, vint top, vint& totalHeight) { - vint cx=0; - vint cy=top; + vint cx = 0; + vint cy = top; virtualLines.Clear(); - if(scriptRuns.Count()==0) + if (scriptRuns.Count() == 0) { // if this line doesn't contains any run, skip and render a blank line - vint height=(vint)(documentFragments[0]->fontStyle.size*1.5); - bounds=Rect(Point(cx, cy), Size(0, height)); - cy+=height; + vint height = (vint)(documentFragments[0]->fontStyle.size * 1.5); + bounds = Rect(Point(cx, cy), Size(0, height)); + cy += height; } else { @@ -11319,122 +11339,132 @@ UniscribeLine } // render this line into lines with auto line wrapping - vint startRun=0; - vint startRunOffset=0; - vint lastRun=0; - vint lastRunOffset=0; - vint currentWidth=0; + vint startRun = 0; + vint startRunOffset = 0; + vint lastRun = 0; + vint lastRunOffset = 0; + vint currentWidth = 0; - while(startRunSearchForLineBreak(lastRunOffset, availableWidth-currentWidth, firstRun, charLength, charAdvances); - firstRun=false; + vint charLength = 0; + vint charAdvances = 0; + UniscribeRun* run = scriptRuns[i].Obj(); + run->SearchForLineBreak(lastRunOffset, wrapLine, availableWidth - currentWidth, firstRun, charLength, charAdvances); + firstRun = false; - if(charLength==run->length-lastRunOffset) + if (charLength == run->length - lastRunOffset) { - lastRun=i+1; - lastRunOffset=0; - currentWidth+=charAdvances; + lastCompletedRunOffset = lastRunOffset; + lastRun = i + 1; + lastRunOffset = 0; + currentWidth += charAdvances; } else { - lastRun=i; - lastRunOffset=lastRunOffset+charLength; + if (lastRunOffset == 0 && charLength == 0) + { + lastRun--; + lastRunOffset = lastCompletedRunOffset; + } + else + { + lastRun = i; + lastRunOffset = lastRunOffset + charLength; + } break; } } // if the range is empty, than this should be the end of line, ignore it - if(startRunSumHeight(); - if(maxHeightSumHeight(); + if (maxHeight < size) maxHeight = size; } { - vint size=scriptRuns[i]->SumTextHeight(); - if(maxTextHeightSumTextHeight(); + if (maxTextHeight < size) maxTextHeight = size; } } // determine the rendering order for all runs inside this range - Array levels(availableLastRun-startRun+1); + Array levels(availableLastRun - startRun + 1); Array runVisualToLogical(levels.Count()); Array runLogicalToVisual(levels.Count()); - for(vint i=startRun;i<=availableLastRun;i++) + for (vint i = startRun; i <= availableLastRun; i++) { - levels[i-startRun]=scriptRuns[i]->scriptItem->scriptItem.a.s.uBidiLevel; + levels[i - startRun] = scriptRuns[i]->scriptItem->scriptItem.a.s.uBidiLevel; } ScriptLayout((int)levels.Count(), &levels[0], &runVisualToLogical[0], &runLogicalToVisual[0]); // render all runs inside this range - vint startRunFragmentCount=-1; - for(vint i=startRun;i<=availableLastRun;i++) + vint startRunFragmentCount = -1; + for (vint i = startRun; i <= availableLastRun; i++) { - vint runIndex=runVisualToLogical[i-startRun]+startRun; - UniscribeRun* run=scriptRuns[runIndex].Obj(); - vint start=runIndex==startRun?startRunOffset:0; - vint end=runIndex==lastRun?lastRunOffset:run->length; - vint length=end-start; + vint runIndex = runVisualToLogical[i - startRun] + startRun; + UniscribeRun* run = scriptRuns[runIndex].Obj(); + vint start = runIndex == startRun ? startRunOffset : 0; + vint end = runIndex == lastRun ? lastRunOffset : run->length; + vint length = end - start; - if(runIndex==startRun) + if (runIndex == startRun) { - startRunFragmentCount=run->fragmentBounds.Count(); + startRunFragmentCount = run->fragmentBounds.Count(); } UniscribeRun::RunFragmentBounds fragmentBounds; - fragmentBounds.startFromRun=start; - fragmentBounds.length=length; - fragmentBounds.bounds=Rect( - Point(cx, cy+maxHeight-run->SumHeight()), + fragmentBounds.startFromRun = start; + fragmentBounds.length = length; + fragmentBounds.bounds = Rect( + Point(cx, cy + maxHeight - run->SumHeight()), Size(run->SumWidth(start, length), run->SumHeight()) - ); + ); run->fragmentBounds.Add(fragmentBounds); - cx+=run->SumWidth(start, length); + cx += run->SumWidth(start, length); } // adjust alignment - vint cxOffset=0; - switch(alignment) + vint cxOffset = 0; + switch (alignment) { case Alignment::Center: - cxOffset=(availableWidth-cx)/2; + cxOffset = (availableWidth - cx) / 2; break; case Alignment::Right: - cxOffset=availableWidth-cx; + cxOffset = availableWidth - cx; break; } // shift all bounds using alignment - if(cxOffset!=0) + if (cxOffset != 0) { - for(vint i=startRun;i<=availableLastRun;i++) + for (vint i = startRun; i <= availableLastRun; i++) { - UniscribeRun* run=scriptRuns[i].Obj(); - for(vint j=(i==startRun?startRunFragmentCount:0);jfragmentBounds.Count();j++) + UniscribeRun* run = scriptRuns[i].Obj(); + for (vint j = (i == startRun ? startRunFragmentCount : 0); j < run->fragmentBounds.Count(); j++) { - UniscribeRun::RunFragmentBounds& fragmentBounds=run->fragmentBounds[j]; - fragmentBounds.bounds.x1+=cxOffset; - fragmentBounds.bounds.x2+=cxOffset; + UniscribeRun::RunFragmentBounds& fragmentBounds = run->fragmentBounds[j]; + fragmentBounds.bounds.x1 += cxOffset; + fragmentBounds.bounds.x2 += cxOffset; } } } @@ -11442,71 +11472,71 @@ UniscribeLine // create a virtual line { auto virtualLine = Ptr(new UniscribeVirtualLine); - virtualLine->firstRunIndex=startRun; - virtualLine->firstRunBoundsIndex=startRunFragmentCount; - virtualLine->lastRunIndex=availableLastRun; - virtualLine->lastRunBoundsIndex=scriptRuns[availableLastRun]->fragmentBounds.Count()-1; + virtualLine->firstRunIndex = startRun; + virtualLine->firstRunBoundsIndex = startRunFragmentCount; + virtualLine->lastRunIndex = availableLastRun; + virtualLine->lastRunBoundsIndex = scriptRuns[availableLastRun]->fragmentBounds.Count() - 1; - UniscribeRun* firstRun=scriptRuns[virtualLine->firstRunIndex].Obj(); - UniscribeRun* lastRun=scriptRuns[virtualLine->lastRunIndex].Obj(); - UniscribeRun::RunFragmentBounds& firstBounds=firstRun->fragmentBounds[virtualLine->firstRunBoundsIndex]; - UniscribeRun::RunFragmentBounds& lastBounds=lastRun->fragmentBounds[virtualLine->lastRunBoundsIndex]; - - virtualLine->startFromLine=firstRun->startFromLine+firstBounds.startFromRun; - virtualLine->length=lastRun->startFromLine+lastBounds.startFromRun+lastBounds.length-virtualLine->startFromLine; - virtualLine->runText=lineText.Buffer()+virtualLine->startFromLine; + UniscribeRun* firstRun = scriptRuns[virtualLine->firstRunIndex].Obj(); + UniscribeRun* lastRun = scriptRuns[virtualLine->lastRunIndex].Obj(); + UniscribeRun::RunFragmentBounds& firstBounds = firstRun->fragmentBounds[virtualLine->firstRunBoundsIndex]; + UniscribeRun::RunFragmentBounds& lastBounds = lastRun->fragmentBounds[virtualLine->lastRunBoundsIndex]; - bool updateBounds=false; - for(vint i=startRun;i<=availableLastRun;i++) + virtualLine->startFromLine = firstRun->startFromLine + firstBounds.startFromRun; + virtualLine->length = lastRun->startFromLine + lastBounds.startFromRun + lastBounds.length - virtualLine->startFromLine; + virtualLine->runText = lineText.Buffer() + virtualLine->startFromLine; + + bool updateBounds = false; + for (vint i = startRun; i <= availableLastRun; i++) { - UniscribeRun* run=scriptRuns[i].Obj(); - for(vint j=(i==startRun?startRunFragmentCount:0);jfragmentBounds.Count();j++) + UniscribeRun* run = scriptRuns[i].Obj(); + for (vint j = (i == startRun ? startRunFragmentCount : 0); j < run->fragmentBounds.Count(); j++) { - UniscribeRun::RunFragmentBounds& fragmentBounds=run->fragmentBounds[j]; - if(updateBounds) + UniscribeRun::RunFragmentBounds& fragmentBounds = run->fragmentBounds[j]; + if (updateBounds) { - if(virtualLine->bounds.x1>fragmentBounds.bounds.x1) virtualLine->bounds.x1=fragmentBounds.bounds.x1; - if(virtualLine->bounds.x2bounds.x2=fragmentBounds.bounds.x2; - if(virtualLine->bounds.y1>fragmentBounds.bounds.y1) virtualLine->bounds.y1=fragmentBounds.bounds.y1; - if(virtualLine->bounds.y2bounds.y2=fragmentBounds.bounds.y2; + if (virtualLine->bounds.x1 > fragmentBounds.bounds.x1) virtualLine->bounds.x1 = fragmentBounds.bounds.x1; + if (virtualLine->bounds.x2 < fragmentBounds.bounds.x2) virtualLine->bounds.x2 = fragmentBounds.bounds.x2; + if (virtualLine->bounds.y1 > fragmentBounds.bounds.y1) virtualLine->bounds.y1 = fragmentBounds.bounds.y1; + if (virtualLine->bounds.y2 < fragmentBounds.bounds.y2) virtualLine->bounds.y2 = fragmentBounds.bounds.y2; } else { - virtualLine->bounds=fragmentBounds.bounds; - updateBounds=true; + virtualLine->bounds = fragmentBounds.bounds; + updateBounds = true; } } } virtualLines.Add(virtualLine); } - cx=0; - cy+=(vint)(maxHeight + maxTextHeight*0.5); + cx = 0; + cy += (vint)(maxHeight + maxTextHeight * 0.5); } - startRun=lastRun; - startRunOffset=lastRunOffset; + startRun = lastRun; + startRunOffset = lastRunOffset; } // calculate line bounds - vint minX=0; - vint minY=top; - vint maxX=0; - vint maxY=top; + vint minX = 0; + vint minY = top; + vint maxX = 0; + vint maxY = top; for (auto run : scriptRuns) { for (auto fragmentBounds : run->fragmentBounds) { - Rect bounds=fragmentBounds.bounds; - if(minX>bounds.Left()) minX=bounds.Left(); - if(minY>bounds.Top()) minX=bounds.Top(); - if(maxX bounds.Left()) minX = bounds.Left(); + if (minY > bounds.Top()) minX = bounds.Top(); + if (maxX < bounds.Right()) maxX = bounds.Right(); + if (maxY < bounds.Bottom()) maxY = bounds.Bottom(); } } - bounds=Rect(minX, minY, maxX, maxY); + bounds = Rect(minX, minY, maxX, maxY); } - totalHeight=cy; + totalHeight = cy; } void UniscribeLine::Render(UniscribeRun::IRendererCallback* callback, vint offsetX, vint offsetY, bool renderBackground) @@ -11525,7 +11555,7 @@ UniscribeParagraph ***********************************************************************/ UniscribeParagraph::UniscribeParagraph() - :wrapLine(true) + :lastWrapLine(true) ,lastAvailableWidth(-1) ,paragraphAlignment(Alignment::Left) ,built(false) @@ -11638,48 +11668,54 @@ UniscribeParagraph (Initialization) return true; } - void UniscribeParagraph::Layout(vint availableWidth, Alignment alignment) + void UniscribeParagraph::Layout(bool wrapLine, vint availableWidth, Alignment alignment) { - if(lastAvailableWidth==availableWidth && paragraphAlignment==alignment) + if (lastWrapLine == wrapLine && lastAvailableWidth == availableWidth && paragraphAlignment == alignment) { return; } - lastAvailableWidth=availableWidth; - paragraphAlignment=alignment; + lastWrapLine = wrapLine; + lastAvailableWidth = availableWidth; + paragraphAlignment = alignment; - vint cy=0; + if (lastAvailableWidth == -1) + { + return; + } + + vint cy = 0; for (auto line : lines) { - line->Layout(availableWidth, alignment, cy, cy); + line->Layout(wrapLine, availableWidth, alignment, cy, cy); } // calculate paragraph bounds - vint minX=0; - vint minY=0; - vint maxX=0; - vint maxY=0; + vint minX = 0; + vint minY = 0; + vint maxX = 0; + vint maxY = 0; for (auto line : lines) { - Rect bounds=line->bounds; - if(minX>bounds.Left()) minX=bounds.Left(); - if(minY>bounds.Top()) minX=bounds.Top(); - if(maxXbounds; + if (minX > bounds.Left()) minX = bounds.Left(); + if (minY > bounds.Top()) minX = bounds.Top(); + if (maxX < bounds.Right()) maxX = bounds.Right(); + if (maxY < bounds.Bottom()) maxY = bounds.Bottom(); } - vint offsetY=0; + vint offsetY = 0; for (auto line : lines) { for (auto fragment : line->documentFragments) { - vint size=fragment->fontStyle.size/3; - if(size>offsetY) + vint size = fragment->fontStyle.size / 3; + if (size > offsetY) { - offsetY=size; + offsetY = size; } } } - bounds=Rect(minX, minY, maxX, maxY+offsetY); + bounds = Rect(minX, minY, maxX, maxY + offsetY); } void UniscribeParagraph::Render(UniscribeRun::IRendererCallback* callback, bool renderBackground) @@ -12122,118 +12158,174 @@ UniscribeParagraph (Caret Helper) } } + void UniscribeParagraph::GetRunIndexFromTextPos(vint textPos, vint lineIndex, vint& frontRun, vint& backRun) + { + frontRun = -1; + backRun = -1; + if (!IsValidTextPos(textPos)) return; + if (lineIndex < 0 || lineIndex >= lines.Count()) return; + + Ptr line = lines[lineIndex]; + vint start = 0; + vint end = line->scriptRuns.Count() - 1; + while (start <= end) + { + vint middle = (start + end) / 2; + Ptr run = line->scriptRuns[middle]; + vint lineStart = line->startFromParagraph + run->startFromLine; + vint lineEnd = line->startFromParagraph + run->startFromLine + run->length; + if (textPos < lineStart) + { + end = middle - 1; + } + else if (textPos > lineEnd) + { + start = middle + 1; + } + else if (textPos == lineStart) + { + frontRun = middle == 0 ? 0 : middle - 1; + backRun = middle; + return; + } + else if (textPos == lineEnd) + { + frontRun = middle; + backRun = middle == line->scriptItems.Count() - 1 ? middle : middle + 1; + return; + } + else + { + frontRun = middle; + backRun = middle; + return; + } + } + } + Rect UniscribeParagraph::GetCaretBoundsWithLine(vint caret, vint lineIndex, vint virtualLineIndex, bool frontSide) { - Ptr line=lines[lineIndex]; - if(line->startFromParagraph<=caret && caret<=line->startFromParagraph+line->lineText.Length()) + Ptr line = lines[lineIndex]; + if (line->startFromParagraph <= caret && caret <= line->startFromParagraph + line->lineText.Length()) { - if(line->lineText==L"") return line->bounds; - Ptr virtualLine=line->virtualLines[virtualLineIndex]; + if (line->lineText == L"") return line->bounds; + Ptr virtualLine = line->virtualLines[virtualLineIndex]; - for(vint i=virtualLine->firstRunIndex;i<=virtualLine->lastRunIndex;i++) + for (vint i = virtualLine->firstRunIndex; i <= virtualLine->lastRunIndex; i++) { - Ptr run=line->scriptRuns[i]; - if(Ptr textRun=run.Cast()) + Ptr run = line->scriptRuns[i]; + vint firstBounds = i == virtualLine->firstRunIndex ? virtualLine->firstRunBoundsIndex : 0; + vint lastBounds = i == virtualLine->lastRunIndex ? virtualLine->lastRunBoundsIndex : run->fragmentBounds.Count() - 1; + + for (vint j = firstBounds; j <= lastBounds; j++) { - vint firstBounds=i==virtualLine->firstRunIndex?virtualLine->firstRunBoundsIndex:0; - vint lastBounds=i==virtualLine->lastRunIndex?virtualLine->lastRunBoundsIndex:run->fragmentBounds.Count()-1; - - for(vint j=firstBounds;j<=lastBounds;j++) + UniscribeRun::RunFragmentBounds& bounds = run->fragmentBounds[j]; + vint boundsStart = line->startFromParagraph + run->startFromLine + bounds.startFromRun; + if (boundsStart == caret) { - UniscribeRun::RunFragmentBounds& bounds=run->fragmentBounds[j]; - vint boundsStart=line->startFromParagraph+run->startFromLine+bounds.startFromRun; - if(boundsStart==caret) + if (!frontSide || i == virtualLine->firstRunIndex && j == virtualLine->firstRunBoundsIndex) { - if(!frontSide || i==virtualLine->firstRunIndex && j==virtualLine->firstRunBoundsIndex) + if (run->scriptItem->scriptItem.a.fRTL) { - if(run->scriptItem->scriptItem.a.fRTL) - { - return Rect(bounds.bounds.x2, bounds.bounds.y1, bounds.bounds.x2, bounds.bounds.y2); - } - else - { - return Rect(bounds.bounds.x1, bounds.bounds.y1, bounds.bounds.x1, bounds.bounds.y2); - } + return Rect(bounds.bounds.x2, bounds.bounds.y1, bounds.bounds.x2, bounds.bounds.y2); + } + else + { + return Rect(bounds.bounds.x1, bounds.bounds.y1, bounds.bounds.x1, bounds.bounds.y2); } } - else if(caret==boundsStart+bounds.length) + } + else if (caret == boundsStart + bounds.length) + { + if (frontSide || i == virtualLine->lastRunIndex && j == virtualLine->lastRunBoundsIndex) { - if(frontSide || i==virtualLine->lastRunIndex && j==virtualLine->lastRunBoundsIndex) + if (run->scriptItem->scriptItem.a.fRTL) { - if(run->scriptItem->scriptItem.a.fRTL) - { - return Rect(bounds.bounds.x1, bounds.bounds.y1, bounds.bounds.x1, bounds.bounds.y2); - } - else - { - return Rect(bounds.bounds.x2, bounds.bounds.y1, bounds.bounds.x2, bounds.bounds.y2); - } + return Rect(bounds.bounds.x1, bounds.bounds.y1, bounds.bounds.x1, bounds.bounds.y2); + } + else + { + return Rect(bounds.bounds.x2, bounds.bounds.y1, bounds.bounds.x2, bounds.bounds.y2); } } - else if(boundsStart textRun = run.Cast()) { - vint accumulatedWidth=0; - vint lastRunChar=bounds.startFromRun; - for(vint i=0;i<=bounds.length;i++) + vint accumulatedWidth = 0; + vint lastRunChar = bounds.startFromRun; + for (vint i = 0; i <= bounds.length; i++) { - vint charIndex=bounds.startFromRun+i; - vint newLastRunChar=lastRunChar; - if(i>0) + vint charIndex = bounds.startFromRun + i; + vint newLastRunChar = lastRunChar; + if (i > 0) { - if(i==bounds.length) + if (i == bounds.length) { - newLastRunChar=charIndex; + newLastRunChar = charIndex; } else { - WORD cluster1=textRun->wholeGlyph.charCluster[charIndex-1]; - WORD cluster2=textRun->wholeGlyph.charCluster[charIndex]; - if(cluster1!=cluster2) + WORD cluster1 = textRun->wholeGlyph.charCluster[charIndex - 1]; + WORD cluster2 = textRun->wholeGlyph.charCluster[charIndex]; + if (cluster1 != cluster2) { - newLastRunChar=charIndex; + newLastRunChar = charIndex; } } } - if(newLastRunChar!=lastRunChar) + if (newLastRunChar != lastRunChar) { - WORD glyph1=0; - WORD glyph2=0; - if(run->scriptItem->scriptItem.a.fRTL) + WORD glyph1 = 0; + WORD glyph2 = 0; + if (run->scriptItem->scriptItem.a.fRTL) { - glyph2=textRun->wholeGlyph.charCluster[lastRunChar]+1; - glyph1=newLastRunChar==run->length?0:textRun->wholeGlyph.charCluster[newLastRunChar]+1; + glyph2 = textRun->wholeGlyph.charCluster[lastRunChar] + 1; + glyph1 = newLastRunChar == run->length ? 0 : textRun->wholeGlyph.charCluster[newLastRunChar] + 1; } else { - glyph1=textRun->wholeGlyph.charCluster[lastRunChar]; - glyph2=newLastRunChar==run->length?(WORD)textRun->wholeGlyph.glyphs.Count():textRun->wholeGlyph.charCluster[newLastRunChar]; + glyph1 = textRun->wholeGlyph.charCluster[lastRunChar]; + glyph2 = newLastRunChar == run->length ? (WORD)textRun->wholeGlyph.glyphs.Count() : textRun->wholeGlyph.charCluster[newLastRunChar]; } - vint glyphWidth=0; - for(WORD g=glyph1;gwholeGlyph.glyphAdvances[g]; + glyphWidth += textRun->wholeGlyph.glyphAdvances[g]; } - accumulatedWidth+=glyphWidth; - lastRunChar=newLastRunChar; + accumulatedWidth += glyphWidth; + lastRunChar = newLastRunChar; - if(line->startFromParagraph+run->startFromLine+lastRunChar==caret) + if (line->startFromParagraph + run->startFromLine + lastRunChar == caret) { - vint x=0; - if(run->scriptItem->scriptItem.a.fRTL) + vint x = 0; + if (run->scriptItem->scriptItem.a.fRTL) { - x=bounds.bounds.x2-accumulatedWidth; + x = bounds.bounds.x2 - accumulatedWidth; } else { - x=bounds.bounds.x1+accumulatedWidth; + x = bounds.bounds.x1 + accumulatedWidth; } return Rect(x, bounds.bounds.y1, x, bounds.bounds.y2); } } } } + else + { + if (frontSide == !run->scriptItem->scriptItem.a.fRTL) + { + return Rect(bounds.bounds.x1, bounds.bounds.y1, bounds.bounds.x1, bounds.bounds.y2); + } + else + { + return Rect(bounds.bounds.x2, bounds.bounds.y1, bounds.bounds.x2, bounds.bounds.y2); + } + } } } } @@ -12612,51 +12704,68 @@ UniscribeParagraph (Caret) vint UniscribeParagraph::GetNearestCaretFromTextPos(vint textPos, bool frontSide) { - if(!IsValidTextPos(textPos)) return -1; - vint frontLine=0; - vint backLine=0; + if (!IsValidTextPos(textPos)) return -1; + vint frontLine = 0; + vint backLine = 0; GetLineIndexFromTextPos(textPos, frontLine, backLine); - if(frontLine==-1 || backLine==-1) return -1; - if(frontLine!=backLine) + if (frontLine == -1 || backLine == -1) return -1; + if (frontLine != backLine) { - return frontSide?textPos-1:textPos+1; + return frontSide ? textPos - 1 : textPos + 1; } - Ptr line=lines[frontLine]; - if(textPos==line->startFromParagraph || textPos==line->startFromParagraph+line->lineText.Length()) + Ptr line = lines[frontLine]; + if (textPos == line->startFromParagraph || textPos == line->startFromParagraph + line->lineText.Length()) { return textPos; } - vint frontItem=-1; - vint backItem=-1; - GetItemIndexFromTextPos(textPos, frontLine, frontItem, backItem); - if(frontItem==-1 || backItem==-1) return -1; - if(frontItem!=backItem) return textPos; - - Ptr item=line->scriptItems[frontItem]; - vint lineTextPos=textPos-line->startFromParagraph; - if(lineTextPos==item->startFromLine) return textPos; - if(lineTextPos==item->startFromLine+item->length) return textPos; - - vint itemTextPos=lineTextPos-item->startFromLine; - if(item->charLogattrs[itemTextPos].fCharStop) return textPos; - - if(frontSide) + vint frontRun = -1; + vint backRun = -1; + GetRunIndexFromTextPos(textPos, frontLine, frontRun, backRun); + if (frontRun == -1 || backRun == -1) return -1; + if (frontRun != backRun) { - for(vint i=itemTextPos-1;i>=0;i--) + return textPos; + } + + Ptr run = line->scriptRuns[frontRun]; + if (auto objectRun = run.Cast()) + { + return frontSide + ? line->startFromParagraph + run->startFromLine + : line->startFromParagraph + run->startFromLine + run->length + ; + } + vint lineTextPos = textPos - line->startFromParagraph; + if (lineTextPos == run->startFromLine) return textPos; + if (lineTextPos == run->startFromLine + run->length) return textPos; + + auto item = run->scriptItem; + vint itemTextPos = lineTextPos - item->startFromLine; + if (item->charLogattrs[itemTextPos].fCharStop) return textPos; + + if (frontSide) + { + for (vint i = itemTextPos - 1; i >= 0; i--) { - if(item->charLogattrs[i].fCharStop) return i+line->startFromParagraph+item->startFromLine; + if (item->charLogattrs[i].fCharStop) + { + return i + line->startFromParagraph + item->startFromLine; + } } - return line->startFromParagraph+item->startFromLine; + return line->startFromParagraph + item->startFromLine; } else { - for(vint i=itemTextPos+1;ilength;i++) + for (vint i = itemTextPos + 1; i < item->length; i++) { - if(item->charLogattrs[i].fCharStop) return i+line->startFromParagraph+item->startFromLine; + if (item->charLogattrs[i].fCharStop) + { + return i + line->startFromParagraph + item->startFromLine; + } } - return line->startFromParagraph+item->startFromLine+item->length; + return line->startFromParagraph + item->startFromLine + item->length; } } diff --git a/Import/GacUI.Windows.h b/Import/GacUI.Windows.h index 6370e1bb..cbd4af98 100644 --- a/Import/GacUI.Windows.h +++ b/Import/GacUI.Windows.h @@ -1261,7 +1261,7 @@ UniscribeRun virtual vint SumWidth(vint charStart, vint charLength)=0; virtual vint SumHeight()=0; virtual vint SumTextHeight()=0; - virtual void SearchForLineBreak(vint tempStart, vint maxWidth, bool firstRun, vint& charLength, vint& charAdvances)=0; + virtual void SearchForLineBreak(vint tempStart, bool wrapLine, vint maxWidth, bool firstRun, vint& charLength, vint& charAdvances)=0; virtual void Render(IRendererCallback* callback, vint fragmentBoundsIndex, vint offsetX, vint offsetY, bool renderBackground)=0; }; @@ -1289,7 +1289,7 @@ UniscribeTextRun vint SumWidth(vint charStart, vint charLength)override; vint SumHeight()override; vint SumTextHeight()override; - void SearchForLineBreak(vint tempStart, vint maxWidth, bool firstRun, vint& charLength, vint& charAdvances)override; + void SearchForLineBreak(vint tempStart, bool wrapLine, vint maxWidth, bool firstRun, vint& charLength, vint& charAdvances)override; void Render(IRendererCallback* callback, vint fragmentBoundsIndex, vint offsetX, vint offsetY, bool renderBackground)override; }; @@ -1310,7 +1310,7 @@ UniscribeElementRun vint SumWidth(vint charStart, vint charLength)override; vint SumHeight()override; vint SumTextHeight()override; - void SearchForLineBreak(vint tempStart, vint maxWidth, bool firstRun, vint& charLength, vint& charAdvances)override; + void SearchForLineBreak(vint tempStart, bool wrapLine, vint maxWidth, bool firstRun, vint& charLength, vint& charAdvances)override; void Render(IRendererCallback* callback, vint fragmentBoundsIndex, vint offsetX, vint offsetY, bool renderBackground)override; }; @@ -1357,7 +1357,7 @@ UniscribeLine void ClearUniscribeData(); bool BuildUniscribeData(WinDC* dc); - void Layout(vint availableWidth, Alignment alignment, vint top, vint& totalHeight); + void Layout(bool wrapLine, vint availableWidth, Alignment alignment, vint top, vint& totalHeight); void Render(UniscribeRun::IRendererCallback* callback, vint offsetX, vint offsetY, bool renderBackground); }; @@ -1376,7 +1376,7 @@ UniscribeParagraph //***************************** Uniscribe Data List> lines; //***************************** Layout Data - bool wrapLine; + bool lastWrapLine; vint lastAvailableWidth; Rect bounds; @@ -1385,7 +1385,7 @@ UniscribeParagraph void ClearUniscribeData(); bool BuildUniscribeData(WinDC* dc); - void Layout(vint availableWidth, Alignment alignment); + void Layout(bool wrapLine, vint availableWidth, Alignment alignment); void Render(UniscribeRun::IRendererCallback* callback, bool renderBackground); void SearchFragment(vint start, vint length, vint& fs, vint& ss, vint& fe, vint& se); @@ -1403,6 +1403,7 @@ UniscribeParagraph void GetLineIndexFromTextPos(vint textPos, vint& frontLine, vint& backLine); void GetVirtualLineIndexFromTextPos(vint textPos, vint lineIndex, vint& frontLine, vint& backLine); void GetItemIndexFromTextPos(vint textPos, vint lineIndex, vint& frontItem, vint& backItem); + void GetRunIndexFromTextPos(vint textPos, vint lineIndex, vint& frontRun, vint& backRun); Rect GetCaretBoundsWithLine(vint caret, vint lineIndex, vint virtualLineIndex, bool frontSide); vint GetCaretFromXWithTextRunBounds(vint x, vint lineIndex, vint runIndex, vint runBoundsIndex); vint GetCaretFromXWithLine(vint x, vint lineIndex, vint virtualLineIndex); diff --git a/Import/GacUI.cpp b/Import/GacUI.cpp index d2b90a5c..ce045a82 100644 --- a/Import/GacUI.cpp +++ b/Import/GacUI.cpp @@ -18697,15 +18697,6 @@ GuiDocumentCommonInterface SetActiveHyperlink(nullptr); } - Point GuiDocumentCommonInterface::GetDocumentViewPosition() - { - return Point(0, 0); - } - - void GuiDocumentCommonInterface::EnsureRectVisible(Rect bounds) - { - } - //================ callback void GuiDocumentCommonInterface::OnStartRender() @@ -18743,11 +18734,120 @@ GuiDocumentCommonInterface //================ basic + void GuiDocumentCommonInterface::UserInput_FixForPlainText(Ptr model, vint beginParagraph, vint endParagraph) + { + if (beginParagraph > endParagraph) return; + + RunRangeMap runRanges; + document_editor::GetRunRange(model->paragraphs[endParagraph].Obj(), runRanges); + + TextPos begin(beginParagraph, 0); + TextPos end(endParagraph, runRanges[model->paragraphs[endParagraph].Obj()].end); + model->ConvertToPlainText(begin, end); + + for (vint i = beginParagraph; i <= endParagraph; i++) + { + model->paragraphs[i]->alignment.Reset(); + } + } + + void GuiDocumentCommonInterface::UserInput_FixForSingleline(collections::List& paragraphTexts) + { + auto line = stream::GenerateToStream([&](stream::StreamWriter& writer) + { + for(auto [paragraph, index] : indexed(paragraphTexts)) + { + if (index > 0 && config.spaceForFlattenedLineBreak) + { + writer.WriteChar(L' '); + } + writer.WriteString(paragraph); + } + }); + paragraphTexts.Clear(); + paragraphTexts.Add(line); + } + + void GuiDocumentCommonInterface::UserInput_FixForSingleline(Ptr model) + { + auto firstParagraph = model->paragraphs[0]; + for (auto paragraph : From(model->paragraphs).Skip(1)) + { + if (config.spaceForFlattenedLineBreak) + { + auto textRun = Ptr(new DocumentTextRun); + textRun->text = WString::Unmanaged(L" "); + firstParagraph->runs.Add(textRun); + } + CopyFrom(firstParagraph->runs, paragraph->runs, true); + } + model->paragraphs.Clear(); + model->paragraphs.Add(firstParagraph); + } + + void GuiDocumentCommonInterface::UserInput_FixForNonParagraph(WString& text) + { + text = stream::GenerateToStream([&](stream::StreamWriter& writer) + { + for (vint j = 0; j < text.Length(); j++) + { + if (text[j] == L'\n') + { + if (config.spaceForFlattenedLineBreak) + { + writer.WriteChar(L' '); + } + } + else if (text[j] != L'\r') + { + writer.WriteChar(text[j]); + } + } + }); + } + + void GuiDocumentCommonInterface::UserInput_FixForNonParagraph(Ptr paragraph) + { + List> containers; + containers.Add(paragraph); + + for (vint i = 0; i < containers.Count(); i++) + { + auto container = containers[i]; + for (auto run : container->runs) + { + if (auto subContainer = run.Cast()) + { + containers.Add(subContainer); + } + else if (auto textRun = run.Cast()) + { + UserInput_FixForNonParagraph(textRun->text); + } + } + } + } + WString GuiDocumentCommonInterface::UserInput_ConvertDocumentToText(Ptr model) { return model->GetTextForReading(WString::Unmanaged(config.doubleLineBreaksBetweenParagraph ? L"\r\n\r\n" : L"\r\n")); } + void GuiDocumentCommonInterface::UserInput_FormatText(collections::List& paragraphTexts) + { + if (config.paragraphMode != GuiDocumentParagraphMode::Paragraph) + { + for (vint i = 0; i < paragraphTexts.Count(); i++) + { + UserInput_FixForNonParagraph(paragraphTexts[i]); + } + } + if (config.paragraphMode == GuiDocumentParagraphMode::Singleline) + { + UserInput_FixForSingleline(paragraphTexts); + } + } + void GuiDocumentCommonInterface::UserInput_FormatText(const WString& text, collections::List& paragraphTexts) { stream::StringReader reader(text); @@ -18800,22 +18900,9 @@ GuiDocumentCommonInterface { paragraphTexts.Add(paragraph); } - if (config.paragraphMode == GuiDocumentParagraphMode::Singleline) { - auto line = stream::GenerateToStream([&](stream::StreamWriter& writer) - { - for(auto [paragraph, index] : indexed(paragraphTexts)) - { - if (index > 0 && config.spaceForFlattenedLineBreak) - { - writer.WriteChar(L' '); - } - writer.WriteString(paragraph); - } - }); - paragraphTexts.Clear(); - paragraphTexts.Add(line); + UserInput_FixForSingleline(paragraphTexts); } } @@ -18824,21 +18911,7 @@ GuiDocumentCommonInterface if (!model) return; if (config.pasteAsPlainText) { - if (model->paragraphs.Count() > 0) - { - RunRangeMap runRanges; - vint lastParagraphIndex = model->paragraphs.Count() - 1; - document_editor::GetRunRange(model->paragraphs[lastParagraphIndex].Obj(), runRanges); - - TextPos begin(0, 0); - TextPos end(lastParagraphIndex, runRanges[model->paragraphs[lastParagraphIndex].Obj()].end); - model->ConvertToPlainText(begin, end); - - for (auto paragraph : model->paragraphs) - { - paragraph->alignment.Reset(); - } - } + UserInput_FixForPlainText(model, 0, model->paragraphs.Count() - 1); if (baselineDocument) { @@ -18857,60 +18930,14 @@ GuiDocumentCommonInterface if (config.paragraphMode != GuiDocumentParagraphMode::Paragraph) { - List> containers; for (auto paragraph : model->paragraphs) { - containers.Add(paragraph); - } - - for (vint i = 0; i < containers.Count(); i++) - { - auto container = containers[i]; - for (auto run : container->runs) - { - if (auto subContainer = run.Cast()) - { - containers.Add(subContainer); - } - else if (auto textRun = run.Cast()) - { - textRun->text = stream::GenerateToStream([&](stream::StreamWriter& writer) - { - for (vint j = 0; j < textRun->text.Length(); j++) - { - if (textRun->text[j] == L'\n') - { - if (config.spaceForFlattenedLineBreak) - { - writer.WriteChar(L' '); - } - } - else if (textRun->text[j] != L'\r') - { - writer.WriteChar(textRun->text[j]); - } - } - }); - } - } + UserInput_FixForNonParagraph(paragraph); } } - if (config.paragraphMode == GuiDocumentParagraphMode::Singleline) { - auto firstParagraph = model->paragraphs[0]; - for(auto paragraph:From(model->paragraphs).Skip(1)) - { - if (config.spaceForFlattenedLineBreak) - { - auto textRun = Ptr(new DocumentTextRun); - textRun->text = WString::Unmanaged(L" "); - firstParagraph->runs.Add(textRun); - } - CopyFrom(firstParagraph->runs, paragraph->runs, true); - } - model->paragraphs.Clear(); - model->paragraphs.Add(firstParagraph); + UserInput_FixForSingleline(model); } } @@ -19027,11 +19054,44 @@ GuiDocumentCommonInterface void GuiDocumentCommonInterface::NotifyParagraphUpdated(vint index, vint oldCount, vint newCount, bool updatedText) { - documentElement->NotifyParagraphUpdated(index, oldCount, newCount, updatedText); +#define ERROR_MESSAGE_PREFIX L"vl::presentation::controls::GuiDocumentCommonInterface::NotifyParagraphUpdated(vint, vint, vint, bool)#" + auto model = documentElement->GetDocument(); + if (config.paragraphMode == GuiDocumentParagraphMode::Singleline) + { + CHECK_ERROR(model->paragraphs.Count() <= 1, ERROR_MESSAGE_PREFIX L"In Singleline mode, there should be no more than 1 paragraphs in the document."); + } + + if (0 <= index && index + newCount <= model->paragraphs.Count()) + { + if (config.pasteAsPlainText && updatedText) + { + UserInput_FixForPlainText(model, index, index + newCount - 1); + if (baselineDocument) + { + CopyFrom(model->styles, baselineDocument->styles); + } + else + { + model->styles.Clear(); + } + } + + if (config.paragraphMode != GuiDocumentParagraphMode::Paragraph) + { + for (vint i = index; i < index + newCount; i++) + { + UserInput_FixForNonParagraph(model->paragraphs[i]); + } + } + + documentElement->NotifyParagraphUpdated(index, oldCount, newCount, updatedText); + } +#undef ERROR_MESSAGE_PREFIX } void GuiDocumentCommonInterface::EditRun(TextPos begin, TextPos end, Ptr model, bool copy) { + UserInput_FormatDocument(model); EditTextInternal(begin, end, [=, this](TextPos begin, TextPos end, vint& paragraphCount, vint& lastParagraphLength) { documentElement->EditRun(begin, end, model, copy); @@ -19044,14 +19104,26 @@ GuiDocumentCommonInterface { EditTextInternal(begin, end, [=, this, &text](TextPos begin, TextPos end, vint& paragraphCount, vint& lastParagraphLength) { - documentElement->EditText(begin, end, frontSide, text); - paragraphCount=text.Count(); - lastParagraphLength=paragraphCount==0?0:text[paragraphCount-1].Length(); + Array updatedText; + bool useUpdatedText = config.paragraphMode != GuiDocumentParagraphMode::Paragraph; + if (useUpdatedText) + { + List paragraphTexts; + CopyFrom(paragraphTexts, text); + UserInput_FormatText(paragraphTexts); + CopyFrom(updatedText, paragraphTexts); + } + + const Array& textToUse = useUpdatedText ? updatedText : text; + documentElement->EditText(begin, end, frontSide, textToUse); + paragraphCount = textToUse.Count(); + lastParagraphLength = paragraphCount == 0 ? 0 : textToUse[paragraphCount - 1].Length(); }); } void GuiDocumentCommonInterface::EditStyle(TextPos begin, TextPos end, Ptr style) { + if (config.pasteAsPlainText) return; EditStyleInternal(begin, end, [=, this](TextPos begin, TextPos end) { documentElement->EditStyle(begin, end, style); @@ -19060,6 +19132,7 @@ GuiDocumentCommonInterface void GuiDocumentCommonInterface::EditImage(TextPos begin, TextPos end, Ptr image) { + if (config.pasteAsPlainText) return; EditTextInternal(begin, end, [=, this](TextPos begin, TextPos end, vint& paragraphCount, vint& lastParagraphLength) { documentElement->EditImage(begin, end, image); @@ -19070,6 +19143,7 @@ GuiDocumentCommonInterface void GuiDocumentCommonInterface::EditHyperlink(vint paragraphIndex, vint begin, vint end, const WString& reference, const WString& normalStyleName, const WString& activeStyleName) { + if (config.pasteAsPlainText) return; EditStyleInternal(TextPos(paragraphIndex, begin), TextPos(paragraphIndex, end), [=, this](TextPos begin, TextPos end) { documentElement->EditHyperlink(begin.row, begin.column, end.column, reference, normalStyleName, activeStyleName); @@ -19078,6 +19152,7 @@ GuiDocumentCommonInterface void GuiDocumentCommonInterface::RemoveHyperlink(vint paragraphIndex, vint begin, vint end) { + if (config.pasteAsPlainText) return; EditStyleInternal(TextPos(paragraphIndex, begin), TextPos(paragraphIndex, end), [=, this](TextPos begin, TextPos end) { documentElement->RemoveHyperlink(begin.row, begin.column, end.column); @@ -19086,6 +19161,7 @@ GuiDocumentCommonInterface void GuiDocumentCommonInterface::EditStyleName(TextPos begin, TextPos end, const WString& styleName) { + if (config.pasteAsPlainText) return; EditStyleInternal(begin, end, [=, this](TextPos begin, TextPos end) { documentElement->EditStyleName(begin, end, styleName); @@ -19094,6 +19170,7 @@ GuiDocumentCommonInterface void GuiDocumentCommonInterface::RemoveStyleName(TextPos begin, TextPos end) { + if (config.pasteAsPlainText) return; EditStyleInternal(begin, end, [=, this](TextPos begin, TextPos end) { documentElement->RemoveStyleName(begin, end); @@ -19102,6 +19179,7 @@ GuiDocumentCommonInterface void GuiDocumentCommonInterface::RenameStyle(const WString& oldStyleName, const WString& newStyleName) { + if (config.pasteAsPlainText) return; documentElement->RenameStyle(oldStyleName, newStyleName); // submit redo-undo @@ -19113,12 +19191,21 @@ GuiDocumentCommonInterface void GuiDocumentCommonInterface::ClearStyle(TextPos begin, TextPos end) { + if (config.pasteAsPlainText) return; EditStyleInternal(begin, end, [=, this](TextPos begin, TextPos end) { documentElement->ClearStyle(begin, end); }); } + void GuiDocumentCommonInterface::ConvertToPlainText(TextPos begin, TextPos end) + { + EditStyleInternal(begin, end, [=, this](TextPos begin, TextPos end) + { + documentElement->ConvertToPlainText(begin, end); + }); + } + Ptr GuiDocumentCommonInterface::SummarizeStyle(TextPos begin, TextPos end) { if (begin>end) @@ -19472,7 +19559,8 @@ GuiDocumentViewer Point GuiDocumentViewer::GetDocumentViewPosition() { - return GetViewBounds().LeftTop(); + // the document does not move in containerComposition + return { 0,0 }; } void GuiDocumentViewer::EnsureRectVisible(Rect bounds) @@ -33854,6 +33942,25 @@ GuiDocumentElement } } + void GuiDocumentElement::ConvertToPlainText(TextPos begin, TextPos end) + { + if (auto elementRenderer = renderer.Cast()) + { + if (begin > end) + { + TextPos temp = begin; + begin = end; + end = temp; + } + + if (document->ConvertToPlainText(begin, end)) + { + elementRenderer->NotifyParagraphUpdated(begin.row, end.row - begin.row + 1, end.row - begin.row + 1, false); + } + InvokeOnCompositionStateChanged(); + } + } + Ptr GuiDocumentElement::SummarizeStyle(TextPos begin, TextPos end) { if (auto elementRenderer = renderer.Cast()) diff --git a/Import/GacUI.h b/Import/GacUI.h index d88536d7..a33c77d5 100644 --- a/Import/GacUI.h +++ b/Import/GacUI.h @@ -13113,6 +13113,10 @@ Rich Content Document (element) /// The begin position of the range. /// The end position of the range. void ClearStyle(TextPos begin, TextPos end); + /// Clear all styles and remove non-text contents in a specified range. + /// The begin position of the range. + /// The end position of the range. + void ConvertToPlainText(TextPos begin, TextPos end); /// Summarize the text style in a specified range. /// The text style summary. /// The begin position of the range. @@ -16851,8 +16855,8 @@ GuiDocumentCommonInterface void OnMouseUp(compositions::GuiGraphicsComposition* sender, compositions::GuiMouseEventArgs& arguments); void OnMouseLeave(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments); - virtual Point GetDocumentViewPosition(); - virtual void EnsureRectVisible(Rect bounds); + virtual Point GetDocumentViewPosition() = 0; + virtual void EnsureRectVisible(Rect bounds) = 0; //================ callback @@ -16862,7 +16866,14 @@ GuiDocumentCommonInterface protected: + void UserInput_FixForPlainText(Ptr model, vint beginParagraph, vint endParagraph); + void UserInput_FixForSingleline(collections::List& paragraphTexts); + void UserInput_FixForSingleline(Ptr model); + void UserInput_FixForNonParagraph(WString& text); + void UserInput_FixForNonParagraph(Ptr paragraph); + WString UserInput_ConvertDocumentToText(Ptr model); + void UserInput_FormatText(collections::List& paragraphTexts); void UserInput_FormatText(const WString& text, collections::List& paragraphTexts); void UserInput_FormatDocument(Ptr model); @@ -16993,6 +17004,10 @@ GuiDocumentCommonInterface /// The begin position of the range. /// The end position of the range. void ClearStyle(TextPos begin, TextPos end); + /// Clear all styles and remove non-text contents in a specified range. + /// The begin position of the range. + /// The end position of the range. + void ConvertToPlainText(TextPos begin, TextPos end); /// Summarize the text style in a specified range. /// The text style summary. /// The begin position of the range. diff --git a/Import/GacUIReflection.cpp b/Import/GacUIReflection.cpp index 172d0866..50d05a85 100644 --- a/Import/GacUIReflection.cpp +++ b/Import/GacUIReflection.cpp @@ -2988,6 +2988,7 @@ Type Declaration (Class) CLASS_MEMBER_METHOD(RemoveStyleName, {L"begin" _ L"end" _ L"image"}) CLASS_MEMBER_METHOD(RenameStyle, {L"oldStyleName" _ L"newStyleName"}) CLASS_MEMBER_METHOD(ClearStyle, {L"begin" _ L"end"}) + CLASS_MEMBER_METHOD(ConvertToPlainText, { L"begin" _ L"end" }) CLASS_MEMBER_METHOD(SummarizeStyle, {L"begin" _ L"end"}) CLASS_MEMBER_METHOD(SummarizeStyleName, { L"begin" _ L"end" }) CLASS_MEMBER_METHOD(SetParagraphAlignments, { L"begin" _ L"end" _ L"alignments" }) @@ -3471,6 +3472,7 @@ Type Declaration (Class) CLASS_MEMBER_METHOD(RemoveStyleName, {L"begin" _ L"end" _ L"image"}) CLASS_MEMBER_METHOD(RenameStyle, {L"oldStyleName" _ L"newStyleName"}) CLASS_MEMBER_METHOD(ClearStyle, {L"begin" _ L"end"}) + CLASS_MEMBER_METHOD(ConvertToPlainText, { L"begin" _ L"end" }) CLASS_MEMBER_METHOD(SummarizeStyle, {L"begin" _ L"end"}) CLASS_MEMBER_METHOD(SummarizeStyleName, { L"begin" _ L"end" }) CLASS_MEMBER_METHOD(SetParagraphAlignment, {L"begin" _ L"end" _ L"alignments"}) diff --git a/Import/Vlpp.cpp b/Import/Vlpp.cpp index fe9f8106..e81f44ed 100644 --- a/Import/Vlpp.cpp +++ b/Import/Vlpp.cpp @@ -1621,6 +1621,7 @@ UnitTest case FailureMode::Copilot: PrintMessage(L"Failures cause immediate exit.", MessageKind::Info); break; + default:; } auto current = testHead; diff --git a/Tools/Reflection32.bin b/Tools/Reflection32.bin index 5a1202e322a94ba5109380931ceed5753d320cdf..ebe1ff88cabec9bbbb8103d9d54b194d97b6821b 100644 GIT binary patch delta 15172 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 delta 14789 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 delta 14789 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 hPJ(Zz@siJ#`TyU;bI-zE9&R7|VlA~F;G_8kBK