Closed scofalik closed 7 years ago
This is how AttributeCommand
could be implemented. Using this you can create commands like bold
, italic
, etc.
I don't know if it is a good idea to have it here, but this is nowhere near final code and I put it here for discussion - whether this conceptions are good and well understood. You can make your point of view and imagine how much will it take to implement certain command. execute
looks like it should be simpler, but there are a few cases we have to handle.
@scofalik : better to move that to a gist, so one can comment about specific lines with ease.
I moved it to gist.
I see a hardcoded "bold" here: https://gist.github.com/scofalik/cde9c5e09f08d5c55a06#file-attributecommand-js-L23
We've been discussing one thing now – how the state of the attribute command depends on the selection. Do you think @fredck that it's a topic for Editor Recommendations?
Exactly... that was one of the things in my mind as well and ER fits well for it.
Whenever me and @pjasiun were talking about this, we always assumed that the collapsed selection has same attributes as the Tree Model node that is right before it. If there are no nodes before selection, selection has same attributes as Tree Model node that is it's parent.
AFAIR we didn't make any statement about non-collapsed selection, but after talking w @Reinmar it seems that the best idea is to base selection attributes on node before / parent node of the beginning of the selection (the visible beginning, not the focus position).
This way would let us easily implement few things.
BTW. we had an idea, that features like bold
, if applied on collapsed selection, will just change selection's attributes. Any typing then will be based on selection's attributes. This way we won't have to create bogus elements to put selection in.
I created an issue for the state thing: https://github.com/ckeditor/editor-recommendations/issues/32
I have some doubts on how AttributeCommand
should work, posted here https://github.com/ckeditor/editor-recommendations/issues/32#issuecomment-183259732
Connected with: https://github.com/ckeditor/editor-recommendations/issues/32#issuecomment-183350092
After F2F discussion with @fredck we decided that, for now, we will abandon saving attributes to parent node and reading them from parent node. It appeared that there are even more problematic and confusing situations if we go with saving attributes to parent.
There is also a concern, that parent might have other attributes which should not be copied to selection attributes. We would have to somehow mark which attributes are for selection.
We came up with alternative solutions, but they also have their drawbacks:
selection:
, so we will know how to handle them,LivePosition
s to mark places where selection should be restored (alternative to virtual-node but may be challenging to implement in collaborative editing).If we ever try to implement saving attributes styles, we have to keep in mind:
Followup for my comment in https://github.com/ckeditor/editor-recommendations/issues/32#issuecomment-184122749:
https://dev.ckeditor.com/ticket/12634 is a very popular issue – it has a lot of DUPs (much more than we had noticed, because it can be reported in many various ways). Let's not repeat the mistakes of browser vendors here. Users expect that styles will be maintained in empty paragraphs and popular editors handle this as expected (I checked MS Word, GDocs and TinyMCE which workarounds the browser behavior).
So there are three use cases which IMO we must handle correctly:
- returning to an empty paragraph by arrow keys,
- returning to an empty paragraph by mouse click,
- returning to an empty paragraph by pressing backspace.
In all of them the styles that the text would have there initially, should be maintained. It's editor's implementator decision how to do this. For CKEditor 5 we'll continue the discussion in ckeditor/ckeditor5-design#128.
We discussed this topic with @pjasiun a long time ago and I think that the idea still makes sense – the attributes that the text typed in an empty block would have should be stored temporarily in its parent (in the block element).
The only issues that such a solution may have are:
As for 1, up to a point it seems to be simple – the implementation of enter key feature will handle copying styles. But what with content insertion? If it was only about typing, then it would be easy, because it would boil down to a single feature. But many features will insert some content, so perhaps some automation needs to be implemented – maybe a post fixer?
As for 2, I'm for defining which attribute is block and which is inline in the schema. This information will not be only needed for the selection (it needs to know which styles to inherit from the block when placed there), but it may quickly turn out that such an information will be needed in other features. Hence, it's best to keep things public – in this case in the schema. (Note: an attribute may only be one of inline style and block style – one attribute should not have a dual nature).
As for 3, AFAIR it's the client who resolves conflicts, so it has the whole knowledge about the schema.
selection_store:bold
attribute or something.I've pushed some code to look at on branch t/features
in ckeditor5-core
https://github.com/ckeditor/ckeditor5-core/tree/t/features
I will add docs and finish tests if we agree that this is a good direction to go.
I am not saying that the idea is bad, I just don't like saying that attribute is inline and then apply it to block element.
For me it's just a way to store these attributes. They represent inline styles, but are kept in a block. Kinda like in CSS where you can apply font-weight
to a block or an inline element. Also note that the converter will need to create an element for such a style, even if it's applied on a block. E.g. when you have a "P" element with a "bold=true" attribute, the bold feature converter will need to make sure that such a structure is created: <p><strong></strong></p>
(see why). If we decide to prefix their names, that will bring confusion for the converters – we'll need there some additional layer deciphering that.
Well, prefixing doesn't make any sense if we had to do something with such attributes as you described. So we just drop the idea.
One more thing – I should explain why do we need <p><strong></strong></p>
. Why the typing feature cannot insert the characters with the right attributes (and then it would be fixed in the DOM)?
The answer is – it could, but:
<p>
element and the engine needs to fix it after the character makes in the loop-back.I definitely agree that we should put <p><strong></strong></p>
there. Wrapping the first letter into <strong>
would break the composition. I think that the selection observer will remove such <strong>
if the selection move, but this is something I prefer not to think about as long as we do not work on the selection.
I updated files on branch t/features
https://github.com/ckeditor/ckeditor5-core/tree/t/features
Look at commits from Feb 18.
@Reinmar
For me it's just a way to store these attributes. They represent inline styles, but are kept in a block. Kinda like in CSS where you can apply font-weight to a block or an inline element. Also note that the converter will need to create an element for such a style, even if it's applied on a block. E.g. when you have a "P" element with a "bold=true" attribute, the bold feature converter will need to make sure that such a structure is created:
(see why). If we decide to prefix their names, that will bring confusion for the converters – we'll need there some additional layer deciphering that.
@scofalik
Well, prefixing doesn't make any sense if we had to do something with such attributes as you described. So we just drop the idea.
I think the original prefix idea was just about having a way to clearly specify what attributes must be moved to the selection when the block is empty. After all, converters need to convert attributes set to the selection only, for example when simply applying "bold" on a collapsed selection.
For example: p (selection:bold=true)
-> moved to selection without prefix if p
is empty -> selection (bold:true)
-> converter works on the selection to produce proper elements -> <p><strong>|</strong></p>
.
I think the original prefix idea was just about having a way to clearly specify what attributes must be moved to the selection when the block is empty.
But for this we can use the schema. I think it is a more future safe solution and give us more flexibility.
:/ but then what, you need to say in the schema that "paragraph" accepts the "bold" attribute in it, even if you don't want this to happen, just to support this technical requirement? And again, we're talking about a way to store selection attributes, not nodes attributes.
I presume that if you allow bold inside paragraph this bold needs to be preserved by the selection, so it should be copied to the block. But that block allowed that style in the first place, so there's no problem.
If the style isn't allowed in some block, then the selection should not preserve it there anyway so nothing needs to be copied.
So there can be two types of attributes applied to a block – those defined as allowed on it and inside it. I agree that's not perfectly clear, but neither is adding some "selection:" attributes and the whole concept with copying attrs to the block :D.
And again, we're talking about a way to store selection attributes, not nodes attributes.
That's right and that makes me unsure what's the best solution. I think that we can change it in the future if one of the ways will work better or not. On the one hand we have those worried which you expressed. On the other we have converters which need to be executed for the attributes with "selection:" prefix, so some additional code somewhere else. Therefore I would say that it'll be hard to decide now what's the best option because we don't have working features and selection yet.
I'm quite OK with both solutions but I would implement myself unprefixed attrs.
Btw, theoretical conflict issue... if the "paragraph" feature would also support the "bold" attribute (for the paragraph node itself), it would be impossible to understand whether the bold attribute in p (bold=true)
is the node attribute or the selection attribute.
Btw, theoretical conflict issue... if the "paragraph" feature would also support the "bold" attribute
Fortunately that's forbidden (we make the rules :D).
But we've just found a case which rather votes for prefixing attributes. Let's say you have an empty inline widget and you press CTRL+B inside it. We rather think that the logic retaining the style in empty elements should apply also to an empty inline widget. This means that the attribute would need to be applied on the <widget>
element. However, the converter wouldn't now know if it's supposed to render that bold inside or outside the widget (you can apply the bold outside). Buuuuut... should the attribute ever be applied outside the widget? In some cases it needs to be (an image e.g.), but... [[[TO BE DISCUSSED]]].
It's amazing how many things we need to consider to make it right. Important points from the discussion we've just had:
<image>
could add a border to the image and bold its caption); those are the situations where having a single attribute in both places makes some sense.<br>
s if we want the selection to go inside them automatically. So instead of adding bogus <br>
directly to the block, it needs to be put inside the innermost inline element inside that block. We checked that this works with up/down arrow keys on Chrome and FF (TODO check Edge). In other cases (left/right arrows, clicks, touch) we can freely post-fix the selection.Similarly to #124 – done, working, will be documented soon.
This issue discusses how an example feature Bold can be described by feature-parts: commands, converters, schema, UI. See also #129.
Feature example: Bold
Bold feature lets a user mark selected text as important (or, simply saying, mark it bold). This is one of the most common features in text editors. If selected text is already marked bold, it should un-bold instead.
Bold initialization and requirements
This feature is very simple and should not have any requirements. Initialization should only create and register proper
Command
s and converters. The feature will facilitate attribute with namebold
only possible valuetrue
.Bold command
Bold should register a single command under name
bold
. The command should be on whenSelection
hasbold
attribute, off otherwise. If command is off, executing it should addbold
attribute to selected elements in Tree Model (or, if selection is collapsed, it should add this attribute to the editor'sSelection
instance). If command was on, it should removebold
attribute from selected elements orSelection
.Bold converters
Tree Model to Tree View converter should check whether processed tree nodes have
bold
attribute. If that's true and processed node is a character, it should be wrapped instrong
element. Note, that the element itself should not have additionalstyle
set.On the other hand, Tree View to Tree Model converter should recognize not only
strong
elements but alsob
elements andspan
elements withstyle="font-weight: bold;"
. Those elements, and characters inside such elements should be added to Tree Model withbold
attribute.Bold schema
bold
attribute can be set to inline and block element (not for widgets, though).bold
attribute should have default priority.Bold UI
Bold feature should create a button which should execute
bold
command.