-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introduce breakBlock
to Composer
.
#3811
Comments
breakElement
to Composer
.breakBlock
to Composer
.
The idea behind those composer's functions (like OTOH, we need to understand which features will extend the enter key behaviour in what ways. E.g. if the list and blockquotes features will modify composer, then how the behaviour that when you press enter in an empty list item that list item is converted into a paragraph is related to "breakBlock" (if that was the method name)? It has nothing to do with breaking anything – it's an enter support. Similarly, a table feature will need to do something with the enter key in table cells and e.g. it may want to move the selection to a next cell. Again, not related to breaking anything – it's an enter support. And remember that executing the enter command should trigger all those behaviours, so in the current state of things you can only implement those modifications in a composer. So, in this case I'm for |
I am not sure if it's the same thing. Let's play devil's advocate. You could look at it this way:
In lists, it would be easier to overwrite 😈 Oh, and, BTW, what do you mean by "supporting enter"? Supporting enter key? Then it should be done using view events. Supporting enter behavior? What really is "enter behavior". Ah, the questions :). But I don't like Maybe |
Hm... So you mean that a list feature should override the whole Similarly, in table cells, the enter should change semantics to Makes sense for me. Then, you'll be able to always use |
The question is why someone would call As I think of it, I think that This also is in vein with what @pjasiun said: "Why would anyone want to use With love, |
BUT. Do we even need So maybe all we need is |
LOL, I feel like discussing with myself :P. We're too open minded ;) We've got 3 layers on which a feature like enter, backspace or typing (there's a ticket about creating a
The first layer lets you react if the behaviour should be changed because of the view implementation. Let's say we have a fake selection which should alter multiple keys and mouse behaviour. The view events would be the place to inject your code. It's a view's feature listening on a view. The second layer represents a feature and its state (command can be disabled). Commands can be seen as the API which a developer could use to build an external UI. And in fact, we also use them in this way – buttons' state is bound to commands and buttons execution execute commands. Commands should reflect editor features such as "pressing enter in the editor", "pressing backspace in the editor", "pressing a letter in the editor", "inserting an image", "linking a text". Those features have a state and action and it seems that commands could be merged into the Finally, we have the engine logic which has to support implementation of composable algorithms which are then used by the commands. However, there's a significant difference between how a command work and such engine methods. A command is a final implementation, so it uses a real selection. The engine methods, in order to be composable, can't use the real selection. They use a static one, so other pieces of code can decide what should really be changed and when. How's that answering our concerns? Well, it isn't really answering all questions, but I needed to draw this mental picture of this architecture in my head :D. Let's see...
It depends. If a developer wants to simulate a real enter key (e.g. we realise that some weird key on a strange layout or an enter button on a onscreen keyboard should have the same behaviour), view event is the answer. Also, it's useful for testing purposes to reproduce user actions in the most precise way. If, however, a developer wants to write a feature in which the logic from the "pressing enter key" action is used, then the engine's method are the answer. Using this layer, the developer has a control over undo steps and moments of rendering a selection (and which selection is actually changed) and perhaps other things as well.
I've got a problem with this piece. At first, it seems that since a different command should be executed, the feature like list should alter the behaviour on the view layer. This sounds right – we change the semantics and fully reuse a different, existing command. This will also apply to case like enter in a table cell and enter on a selected widget. All those actions need to have their respective commands anyway. However, can we go that far to say that for the "enter at the end of a heading" behaviour the heading feature needs to listen on the view? This would mean that we'll need an If we agree that this is OK, then it's clear that we don't need the enter primitive in the engine, because all the alternatives of the default enter behaviour are actually a different commands. This will also mean that what I wrote above regarding the view:
is totally untrue. I'm also fine with that. We can reword this to "controllers listen on the view to perform their action". A feature like a heading is a controller which decides to perform a different action on the enter event. @pjasiun is now super happy. To make sure that we're on the right track, let's consider another scenario. We have a widget. Pressing enter on this widget should enter its options. However, if it's a block widget, we need a way to enter the block after the widget (with keyboard and mouse). So we tell that shift+enter moves to the block after the widget or creates a new one. We have two commands At this point it still all make sense, so I think that we should be safe. Summing up:
So what do we miss to satisfy 3rd point? Add the |
I hope I understood correctly at least 50% of what you wrote.
I can agree that we can divide our code on logical layers, however I am not sure we understand them correctly, so let's sum this up. We have view events. However, just calling this "intention" is a bit dangerous. Of course, user has some kind of intention, but it is not strictly connected with view event. What I mean is that user's intention is not to "press enter". User's intention is to break paragraph. It just happened that, in text editors, enter key always does it. If we had a different key for every intention life would be simpler as we would just map them with commands/composer methods :). So a user has an intention and to realize it, they do an action (key press? mouse click?) in given context (heading? list item? paragraph?). In view events, we are predicting what kind of intentions users could have when editing text and how they realize those intentions
Composer's methods' role is pretty obvious. These are algorithms performing common tasks done on model document. The reasons they are defined are:
So we have user actions (view events) and building blocks (composer's methods). It's obvious that we need:
I still haven't touched describing "Bringing a reaction to pressing enter key and providing a default behavior for it" seems like a good candidate for a For me there's no clear role for this command. If this is "emulating pressing key" then we could fire view event. If it is "breaking current block" we could call I think that the majority of confusion and problems comes from:
Now, for your summary:
As can be seen, there are mutliple approaches to the problem and it's difficult to point to the 100% correct one. However I am happy that we did agree on some things after all. |
My thought, reading your discussion was: how much simpler it would be if we would create a not pluginable code. And I have a feeling that we try too hard to create a limitless pluginable solution. I do not think there is anything wrong having cases where writing plugins will be hard as long as these are not cases we are sure we will implement soon. We will have such cases anyway. BTW: note that the level where you listen to the |
:D Of course, it would be simpler. But we're not creating a "not pluginable code", so we have such discussions. And this is just one of them – it's not an exception. Do I have to remind you who was complaining in CKE4 that a command's state cannot be governed by multiple features at a time? And who proposed a solution for that in CKE5? ;) The case that I mentioned, with copying or not copying block attributes is a real use case – it's been asked multiple times how to achieve this in CKE4. If I have to give up on it already, then it means that there's something wrong with our architecture and the sooner we realise this the better. Especially, that we haven't yet reviewed this aspect.
Yes, later on in my previous post I negated this statement:
|
Intentions are a popular concept which is now being debated by w3c. However, I agree that view events don't make for nice intentions. At the same level we have events like I can't agree with one a thing, though – enter is an intention. It's a key but behaviour of that key is identified with an intention. Just like "delete", it exists on both planes.
I think I can agree with that for now. Let's see where it gets us.
That's where the knowledge about history makes it harder. In CKE4 you'd see commands like So, I see that we have two clear points:
The idea for 2 is missing... and we don't know what to do with commands and intentions. So maybe let's join them? ;) |
We continued the discussion F2F. The summary:
export default class EnterCommand extends Command {
_doExecute() {
const doc = this.editor.document;
doc.enqueueChanges( () => {
enterBlock( doc.batch(), doc.selection, { defaultBlockName: 'paragraph' } );
} );
}
} You'll have a simpler: export default class EnterCommand extends Command {
_enqueueChanges( batch ) {
enterBlock( batch, doc.selection, { defaultBlockName: 'paragraph' } );
}
} |
As far as extending commands is concerned I like the idea of firing additional event by For start, I'll add _execute( param ) {
if ( this.isEnabled ) {
const data = this._doExecute( param );
this.fire( 'afterExecute', data );
}
} This way we won't have any magical methods and we can document what is in |
OTOH, as I look at it now, it will be more complicated to expand command this way. _execute( param ) {
if ( this.isEnabled ) {
const data = this._doExecute( param );
this.fire( 'afterExecute', data );
}
} In this scenario, This means that we need fire _execute( param ) {
if ( this.isEnabled ) {
this.editor.document.enqueueChanges( () => {
const data = this._doExecute( param );
this.fire( 'afterExecute', data );
} );
}
} Then there will be no need to open this block in |
So, final decision (for now ) is that we are adding a I'll put this on hold for a while. This is not-a-small change. Some commands operate on model and feel like they should have this flag set to So, for now, I'll change Then, at the end of iteration 2, we may come back to this issue and finalize it. |
I'm sorry, but it seems that our plan is already outdated: https://github.com/ckeditor/ckeditor5-core/issues/20 :D. |
It turned out that we don't need this in the composer. The enter command became extensible and features should either listen on the view or on the command. |
Yes, after this whole discussion we ended up with doing not much, really. Here is a "follow-up" issue in |
I've described an architecture which makes most sense to me in: #340. |
EnterCommand
introducedenterBlock
method which takesSelection
instance and, briefly speaking, removes contents in that selection and breaks the block in which selection was made. Additionally, it was possible to passdefaultBlockName
option which changed what kind of element was created if selection was at the end of the element (i.e.<h1>
was changed to<p>
).Then,
EnterCommand
when executed, used that function. The callback toenter
event was added:You could pass
defaultBlockName
as execution parameter:editor.execute( 'enter', 'p' );
.It appears that there are two problems concerning this approach:
EnterCommand
behaves in some features. This would lead to putting more stuff/options into the command, which would be used only in single cases.enter
event callback, but it was not possible to overwrite howEnterCommand
works. In other word, in different feature I could do:So it was possible to overwrite what happens after clicking
Enter
key but if other feature wanted to callEnterCommand
, it would have to use the default version (or know about all other features).That's why we decided that it is a good idea to move this whole mechanism to
Composer
and make it work similar todeleteContents
. Then,EnterCommand
can just callComposer#breakBlock
while other features can add their own callbacks. This is more flexible and correct approach.PS:
breakBlock
name is temporary. Maybe there is a better name. However I don't likeenter
in originalenterBlock
name because it is strictly connected withEnter
key and doesn't really describe what the method does.The text was updated successfully, but these errors were encountered: