Open altavir opened 8 years ago
Hello @altavir,
So you're trying to implement a text viewer whose text can be selected and copied but not edited (at least by the user)? Does it have similarities with #175?
Oh, indeed it does (my request is more simple since I do not need text to be editable). Missed that issue (strange, I've searched for "tabstop" ). In fact it is not that complicated to implement. You do not need to introduce tables. All you need to store a list of observable integer tabstop values and replace all \t
character with resizeable gaps with width bound to next tabstops (tabstop minus current column position). One also needs to remember current number of tabulations in line, but it is not hard, really. Then you can either set these tabstops directly, or make some automatic function to calculate them.
I can implement it myself, but I do not know how to insert a gap in the text.
All you need to store [is] a list of observable integer tabstop values and replace all
\t
character with resizeable gaps with width bound to next tabstops (tabstop minus current column position).
The issue there is the "resizeable gaps". It sounds to me like you're suggesting that we replace all \t
characters with either some Node
or some other whitespace character. The former isn't possible yet and may affect other things (line wrapping ?) and the latter would affect plainTextChanges()
and richTextChanges()
.
I'm also assuming that this change would be incorporated into CodeArea
, not StyledTextArea
.
Maybe a Text
node with modified style will do? I tried to apply some styles to \t
character via InlineCssTextArea
. It works (at least for translations), but the problem is that -fx-min-width
is not applicable for Text
node. I have not tried to use scaling instead, but probably it will work. Or perhaps one could extend Text
class to include custom padding.
@altavir I think your goal of having resizeable gaps via nodes can now be done. Custom object support was added and you can design your own version of EditableStyledDocument
.
Thanks, I will check it shortly and report.
I tried to work with 0.7 API but it seems to be unusable or at least overwhelmingly confusing. There are these new Ops
(ops define the type of the segment, maybe it is easier to call them Types
to avoid confusion) classes which I believe in fact are juat a factories and utility implementations for segments (won't it be easier to define abstract Segement
class?).
Old ReadOnlyStyledDocument
is now defined with TextOps
, but there are no implementations for TextOps
even for plain text! If I understand correctly, there should be a singleton class that implements it, but I can't find it.
To say the least, it might help you to read through the Wiki. We've written some pages that explain some things to help people get started.
I tried to work with 0.7 API but it seems to be unusable or at least overwhelmingly confusing.
Unfortunately, it is confusing to a degree. I don't know if I'd say it's unusable.
There are these new Ops (ops define the type of the segment, maybe it is easier to call them Types to avoid confusion) classes
Actually, the Ops
classes provide a way for the area to operate on your segment types (SEG
) since it doesn't know what your type will be.
won't it be easier to define abstract Segement class?
Perhaps it would, but that would then restrict developers. Generics allow them full flexibility as to what a segment is.
Old ReadOnlyStyledDocument is now defined with TextOps, but there are no implementations for TextOps even for plain text! If I understand correctly, there should be a singleton class that implements it, but I can't find it.
If you read through the wiki, implementing such a thing shouldn't be too hard. Still, providing a plain text TextOps
does make sense. However, we would probably need to refactor GenericStyledArea
to become GenericAreaBase
that does not include any style-related things, and then GenericStyledArea
could extend that base and add the style-related things to it. Tomas made such a comment previously in another issue or PR, but I can't recall where.
I wrote the post only after I've read through the wiki. I finally found a way to generate Ops
for text (not in the wiki, but in the examples). It is not obvious since JavaDoc for version 0.7 is sitll pretty empty.
I managed to make my old code work in 0.7. Still found a lot of confusing things. For example necessity to use paragraph style.
Event though I've read through the wiki, I do not completely understand how this object support is working. In fact the only thing I need is ability to place resizeable gaps that would be copied as \t
on text select.
I wrote the post only after I've read through the wiki.
Oh.
I finally found a way to generate Ops for text (not in the wiki, but in the examples).
Mm... looks like we need to still write that part then. I'll add it to the todo list in #403.
It is not obvious since JavaDoc for version 0.7 is sitll pretty empty.
That tends to be the case with Tomas' projects because he believes in self-documenting code.
For example necessity to use paragraph style.
Yeah, just another reason to refactor the base area class to one without styles so that other can add only the styles they want into their area.
Event though I've read through the wiki, I do not completely understand how this object support is working.
You'd need to ask @afester about that as he's the one who wrote it.
In fact the only thing I need is ability to place resizeable gaps that would be copied as \t on text select.
Perhaps what you really need is not custom object support at all, but an EditableStyledDocument
that stores your actual content and another EditableStyledDocument
that maps the first one's content (e.g. turns \t
into a "resizable gap" node). I wonder if a similar approach would be needed to implement something like #107.
I believe that what I really will need is to change a view, not model. Meaning subclassing a GenericStyledArea
. I tried to do it when I first wrote the question. Sadly, it requires much more free time than I have.
By the way, I believe that text model presented in 0.7 is too complicated. Making everything generic is a good thing, but currently it is very hard to understand how it works. You need either write a very good documentation, or hide all the implementation details behind some convenient builders and utilities. I recently made my own attempt to build a more or less universal text model. It is not easy. But it could be made comprehensible.
Any updates on this @altavir The recent release 0.8.1
now includes more documentation.
Thanks for the heads up. Documentation is indeed better. I think it is good idea to link it against JDK using -link
option to increase readability.
I still would like to have tabulated representation, but sadly It is pretty low on my list of priorities right now. I will try to return to it in a month or sow and if it will work, I will get back to you.
In due time, no sooner, no later.
The documentation is still not very comprehensible. I am trying to migrate to 0.8.1
from 0.6.1
and currently I can't understand how to just append styled text to the InlineCssTextArea
. I have style prepared and I just want to append read-only text fragment with this style. Could you provide an example about how to do it?
The documentation is still not very comprehensible.
With this general and rather vague statement, I am not sure whether it refers only to the issue you've just asked for help on (appending a read-only styled text fragment) or something else. Could you be more specific as to what about the documentation is not comprehensible and why? :wink: Otherwise I have no clear outcome to try to reach.
I am trying to migrate to 0.8.1 from 0.6.1 and currently I can't understand how to just append styled text to the InlineCssTextArea. I have style prepared and I just want to append read-only text fragment with this style. Could you provide an example about how to do it?
String text = //
S style = //
int position = area.getLength();
area.replace(position, position, text, style);
Personally, I wonder whether we should remove all the [replace/insert/append]Text
methods because we're dealing with segments now, not text. It would also allow us to remove TextOps
I think, too.
So I have to first add text and the replace it by styled text? Or I must replace empty text with styled text? I am not sure I understand.
As for documentation. If you are saying that is easier to use segments, could you point me to the place in documentation where they are described. I understand that writing good documentation is hard (my own project is much worse than yours), but currently I can't understand even basic concepts of your text model.
As for documentation. If you are saying that is easier to use segments, could you point me to the place in documentation where they are described. I understand that writing good documentation is hard (my own project is much worse than yours), but currently I can't understand even basic concepts of your text model.
I'll explain this first so you have a better understanding of the text model. If this seems very basic and "no duh..." it's because I'm trying to overcommunicate, not because I think you are dumb.
In RichTextFX's original design (this has changed since then, and I'll get to that in a bit), a segment used to be nothing more than a String
for the text part and a Style
(the S
generic) for the style part. This was stored in the class called StyledText<S>
, and allowed one to render rich text. However, the style part and the text part were stored in the same object rather than two separate objects. Thus, the data was stored in a List<StyledText<S>>
object:
Text: a lucky cat caught a mouse when wandering around the house
Style: | red font | italics | red and bold |
List: | index 0 | index 1 | index 2
Since its a text area and not a text field, a Paragraph<S>
object would store this List<StyledText<S>>
object and each paragraph represents one line in the area itself. To represent the entire area's content, a StyledDocument<S>
would store a list of such Paragraphs
(i.e. List<Paragraph<S>>
).
Later on, someone wanted to be able to align the text to the right, so they added paragraph styles (the PS
generic) to the text model. Now, each Paragraph<PS, S>
could be styled so that all of its text could be left/center/right-aligned. This changed StyledDocument<S>
(and related underlying classes) to StyledDocument<PS, S>
.
Later on, someone thought "But a rich text editor should be able to display more than just text! What if we wanted to render an image in-line with the text?" So, they introduced the idea of a segment (the SEG
generic). If one wanted to render both text and images to the area, they could now do so. By making the SEG
object completely generic, so that it does not specify any methods, it allows the developer complete freedom in what that object should be. To still get the methods we need to operate on that segment (e.g. concat, subsequence, length, etc.), the SegmentOps<SEG, S>
(read: segment operations) interface was added. TextOps
extends SegmentOps
by specifing a method which can create a SEG
object with only one String
parameter: public SEG create(String creationInfo)
.
As a result, this changed StyledDocument<PS, S>
to StyledDocument<PS, SEG, S>
. Breaking this down into something like algebraic data types via pseudo-java-code, you have:
public class StyledDocument {
List<Paragraph> paragraphs
public class Paragraph {
PS paragraph style
List<StyledSegment> styledSegs
}
}
I recently introduced a change that makes Paragraph
store the style objects and the segment objects separately, so StyledSegment
isn't quite how its stored, but you get the idea.
But, coming back to your question, what exactly is SEG
? It can be what it was before—text via the String
type, which is what it is in InlineCssTextArea
—that can be styled (via the S
object). Or, it can be a combination of text and images. This can be done by using the type-container-like type, Either
: Either<String, Image>
. Such an object is either the left type (String) or the right type (Image). Theoretically, one could use it to render multiple objects, but this gets rather hectic since Java doesn't support type aliases Either<String, Either<Image, Either<Table, Video>>>
.
S
is either a CSS string via an inline approach (e.g. Node.setStyle("-fx-font-size: 12pt;");
) or a style class approach (e.g. Node.getStyleClasses().add("another-style-class");
). The Wiki explains that more.
I'll now address the first part of your reply:
So I have to first add text and the replace it by styled text? Or I must replace empty text with styled text? I am not sure I understand.
replace(int start, int end, StyledDocument replacement)
will replace the portion of the underlying document from start
to end
with replacement
. When start == end
, nothing in the underlying document is being deleted; only the replacement
is being added. When replacement
is an empty document, only the portion of the document from start
to end
is deleted; nothing is added.
This code
int position = area.getLength();
area.replace(position, position, text, style);
specifies that you are not replacing anything in the document (because position == position
), only adding the text and its style at the end of the document. (the replace(int, int, SEG, S)
method's implementation creates a StyledDocument
with the given segment and style before passing that to the replace
method I mention in the preceding paragraph.
OK, it is much better. Maybe you should place this text into wiki along with some examples of basic operations?
This is sort of already explained (but maybe not quite to the degree of clarity as above) in the model package's package-info.java and in the "Core Classes" wiki page, excluding the history part. Likewise, the HyperlinkDemo package illustrates some of these ideas itself.
When I was writing #630, the PS, SEG, and S generics appear in so many classes. So I put the better explanation in the model package and StyledDocument
-related classes. Otherwise, it felt like I was repeating myself.
Still, I think it would be a good idea to add this to the wiki, perhaps in the "core classes" page?
I think it should have its own entry, maybe between Text Styles and Implementing custom objects ?
Would one of you two mind adding that then?
Ok, I had a shot at it - see: What exactly is SEG ?
Not bad. One thing that should be clarified (which I didn't mention in my response) was that SEG
should be immutable. Since you mention POJOs, I'm wondering whether that implies mutability or not. Also, I wonder if your example of a POJO (e.g. Person) makes sense in this context. RTFX is not a ListView
, so I'm curious myself as to how one would render a Person
object.
Also, this issue is starting to get blurred with another one: explaining what SEG
is. Should we open a new issue to continue discussing it? Or has this been addressed so that we can return to the main issue?
I think that it has been addressed, so we can return to the main issue :-)
As to "how one would render a Person
object", one could for instance display the person's name and surname in a Hyperlink
control, or maybe display a thumbnail pic of the person with ImageView
.
BTW: In my own project when I implemented custom objects I created an AbstractSegment (as @altavir suggested above) that is basically a SEG wrapper, with Ops and node creator methods all wrapped up together. I think I mainly did it this way because I disliked the Either<A,B>
generic all over the place, and I was more comfortable with the Interface/Abstract API pattern.
As to "how one would render a Person object", one could for instance display the person's name and surname in a Hyperlink control, or maybe display a thumbnail pic of the person with ImageView.
Adding that explanation makes a lot more sense to me. Could you update that in the wiki page, too, if you haven't already?
BTW: In my own project when I implemented custom objects I created an AbstractSegment (as @altavir suggested above) that is basically a SEG wrapper, with Ops and node creator methods all wrapped up together. I think I mainly did it this way because I disliked the Either<A,B> generic all over the place, and I was more comfortable with the Interface/Abstract API pattern.
I can see why you would go that route as their are different pros/cons than the Either approach.
Hello, I am trying to implement a text viewer (no editing so far) using RichTextFx with tab stops meaning that I need for text with the same number of tabulation symbols start at the same position in different lines. The only problem is that I do not understand how to insert a gap with variable with inside the text. In TextFlow, I solved this problem by inserting an invisible Box with variable width, and it worked quite fine, since it also allows to change tab stops dynamically (the only problem with TextFlow is that I cannot select and copy text from it). Is there a way to insert gaps in RichTextFX?