Denkansatz für Canvas-basierten Text-Editor

uburoi

Lt. Commander
Registriert
Aug. 2008
Beiträge
1.338
Hallo zusammen!

Vorweg: Das Projekt ist eine selbstgewählte Übung für mich. Die Frage nach dem Warum erübrigt sich also – einfach aus Spaß an der Freude.

Ich möchte einen simplen Terminal-artigen Texteditor schreiben. Dabei ist die Textarea einfach eine leere Canvas; alle zur Verarbeitung und Darstellung des Textes notwendigen Berechnungen soll das Programm bzw. die Canvas-Subklasse selbst durchführen. (Darin liegt für mich die Herausforderung.)
Bei einer älteren Version dieses Projekts, mit dem ich mich vor einer Weile schon einmal beschäftigt habe, hat das auch ganz gut geklappt: Den Text habe ich in einem Buffer zeilenweise gespeichert; neuer Text wurde an der entsprechenden Cursor-Position eingefügt (mit entsprechendem Offset bei vielen und/oder langen Zeilen) und bei jedem Canvas-Refresh wurden nur die in der Area sichtbaren Zeichen dargestellt etc. Das Ganze klappte performant und zuverlässig, hatte aber einen für mich entscheidenden Nachteil: kein Zeilenumbruch.

Nun habe ich nochmal ganz von vorne angefangen (auch um ein paar Algorithmen neu zu schreiben, die mir in der Rückschau zu umständlich erscheinen) und das "Gerüst" geschrieben > siehe Screenshot. Bevor ich mich jetzt an die Verarbeitung von Texteingaben mache, muss ich ersteinmal für mich im Kopf klar haben, wie ich den Text speichere und verarbeite.
Mein größtes Problem: Ich möchte einen automatischen Zeilenumbruch an Leerzeichen-Stellen (soft wrap) implementieren. Daran knüpfen sich für mich mehrere Probleme: Wie speichere ich intern den Text? Den ganzen Text als String vorhalten wäre blöd, da ich dann nach jeder Texteingabe den kompletten Text neu umbrechen müsste, was bei längeren Texten sicherlich zu Verzögerungen führen würde, die ich unbedingt vermeiden möchte. Zeilenweise speichern geht aber auch nicht mehr, da sich die Zeilen ja ständig verändern können.

Ihr seht: Mir fehlt hier eine grundlegende Strategie, wie ich den Text intern handlen soll, damit das Ganze mit möglichst wenig Rechenaufwand (also immer flüssig) funktioniert. Meine bisherige Internet-Recherche hat bislang leider auch wenig ergeben, wenngleich ich nicht sicher bin, ob ich überhaupt nach dem Richtigen gesucht habe.

Vielleicht könnt ihr mir ja mit einem Gedankenanstoß auf die Sprünge helfen. Eine konkrete Programmiersprache habe ich hier übrigens bewusst außen vor gehalten, da es mir nicht um die Implementation geht (die wird nicht das Problem sein, wenn ich ersteinmal weiß, wie ich vorgehen will), sondern um die Grundstrategie.

Herzlichen Dank schon einmal fürs Lesen!
Gruß Jens

Bildschirmfoto 207.png
 
Den String in Zeilen umbrechen, beim Einfügen von Text überprüfen, ob umgebrochen werden muss, weil zu lang geworden. Durch das einfügen des letzten Wortes in die nächste Zeile dafür den selben Prozess anstoßen. Das geht dann so lange, bis eine Zeile da ist, die genug Platz hat ohne weiter umbrechen zu müssen. Du musst also immer nur die aktuelle + evtl. (falls umgebrochen werden muss) die nächste Zeile überprüfen.

Und beim Text entfernen eben umgedreht. Schauen, ob Text aus der nächsten Zeile in die aktuelle passt... und so weiter.
 
Daran habe ich auch schon gedacht.
Aber ist es nicht ein Problem, dass ich beim Laden eines Textes dann erst einmal den kompletten Text umbrechen muss? Bei sehr umfangreichen Dokumenten kann das sicherlich ein Weilchen dauern, könnte ich mir vorstellen.

Gruß Jens
 
Es würde ja reichen, wenn du das nur bis zur aktuellen Position machst und dann beim scrollen das ganze weiter berechnest. Oder wenn du die Textposition anhand der Scrollbar nicht exakt sondern nur näherungsweise bestimmst und dann immer nur an der aktuellen Position das Umbrechen durchführst.
 
Ja, das klingt nach einer interessanten Möglichkeit. Muss ich mir erstmal durch den Kopf gehen lassen, wie ich das umsetzen könnte …

Gruß Jens
 
Ich habe gerade keine Lösung für dich. Ich nutze Visual Studio Code als Texteditor für so ziemlich alles mögliche. Dort kann ich in den Settings den SoftWrap einstellen - immer bei Screenende, fixe Anzahl zeichen, komplett aus und ka was noch sonst für Optionen.

Das Projekt ist auf GitHub und OpenSource vielleicht kannst du da ja rein schauen und dich inspirieren lassen.
 
Danke für den Hinweis, aber ich glaube nicht, dass ich als Hobbybastler bei einem so umfangreichem Programm in einer vertretbaren Zeit durch den Quellcode durchsteige. ☺️

Ich habe gestern noch ein bisschen mit dem oben vorgeschlagenen Absatz herumprobiert, und der Umbruch ließ sich so auch recht einfach umsetzen; allerdings tüftele ich jetzt noch an dem Folgeproblem, dass der Cursor den Umbruch korrekt mitmachen muss. Auch hier muss ich nun etwas umdenken…

Gruß Jens
 
Auf die Idee mit VS Code bin ich gestern auch gekommen und habe in GitHub ne passende Datei gesucht.. Aber in 2 Hände voll Minuten nichts gefunden 😬

Muss man wohl mal klonen und nach "wrap" durchsuchen.

Nachtrag: Und gerade fällt mir ein, VS Code hat ein Canvas basiertes Terminal.. besser nach dem Code suchen.

Eine weitere Alternative wäre wohl noch nano (Terminal Editor unter Linux).
 
So, ein erster Erfolg: Beim Tippen von Buchstaben (Enter, Backspace etc. habe ich erstmal außen vor gelassen) wird für die aktuelle Zeile eine rekursive Funktion aufgerufen, die überhängende Wörter weiterverschiebt. Das funktioniert auch bislang sehr gut und wird mir dann hoffentlich als Vorlage für Weiteres dienen können.
Bisher sieht, falls es interessiert, die Funktion so aus (geschrieben in Xojo, einem Basic-Dialekt) – da lässt sich sicher noch das eine oder andere optimieren, aber immerhin klappt es erstmal:

Code:
Private Sub BreakLine(n As Integer)
 
  If Buffer(n).Length >= Cols Then
    If n = Buffer.LastRowIndex Then
      Buffer.AddRow("")
    Elseif Buffer(n).Right(1) = EndOfLine Then
      Buffer.AddRowAt(n + 1, "")
    End If
    
    Var offset As Integer = Buffer(n).Length - If(Buffer(CY).Right(1) = EndOfLine, 1, 0) - 1
    While offset > 0
      If Buffer(n).Middle(offset, 1) = " " And offset < Cols Then
        Exit While
      End If
      offset = offset - 1
    Wend
    
    Buffer(n + 1) = Buffer(n).Right(Buffer(n).Length - offset - 1) + Buffer(n + 1)
    Buffer(n) = Buffer(n).Left(offset + 1)
    
    BreakLine(n + 1)
  End If
End Sub

Herzlichen Dank bis hierher für eure Hilfe!
Gruß Jens

Bildschirmfoto 210.png
 
  • Gefällt mir
Reaktionen: Bagbag
Die Funktion zum Text entfernen ist noch "handlicher" als gedacht, wobei ich hier die Rekursion schachteln musste, was erstmal ein bisschen Arbeit für mein Hirn war:

Code:
Private Sub BreakLineRemove(n As Integer)
  If n < Buffer.LastRowIndex Then
    Var l As Integer = If(Buffer(n + 1).CountFields(" ") = 1, Buffer(n + 1).Length, Buffer(n + 1).NthField(" ", 1).Length)
    If Buffer(n).Length + l < Cols Then
      If Buffer(n + 1).CountFields(" ") = 1 Then
        Buffer(n) = Buffer(n) + Buffer(n + 1)
        Buffer.RemoveRowAt(n + 1)
      Else
        Buffer(n) = Buffer(n) + Buffer(n + 1).NthField(" ", 1) + " "
        Buffer(n + 1) = Buffer(n + 1).Right(Buffer(n + 1).Length - Buffer(n + 1).NthField(" ", 1).Length - 1)
        BreakLineRemove(n + 1)
        BreakLineRemove(n)
      End If
    End If
  End If
End Sub

Ich denke, ich kann den hier eingeschlagenen Weg tatsächlich weiterverfolgen, wobei nun mit Absätzen und Absatzumbrüchen nochmal ein ordentliches Stück Arbeit wartet. Von daher kann der Thread hier jetzt erstmal zu den Akten …

Gruß Jens
 
Zurück
Oben