Vaadin weird behavior of Tabs after changing tab order - tabs

i have made some reordering each tab in tabs component mechanism but it is working not as i expected and i dont know why. I have some basic example with reordering.
#Route(value = "test")
public class testPanel extends Div {
public newPanel() {
Tabs tabs = new Tabs();
Button btn = new Button("Move 3 to 0");
Button ref = new Button("refresh");
ref.addClickListener(evt ->{
tabs.removeAll();
tabs.add(new Tab("0"));
tabs.add(new Tab("1"));
tabs.add(new Tab("2"));
tabs.add(new Tab("3"));
});
ref.click();
btn.addClickListener(evt -> {
int maxPos= 3;
Tab tab= (Tab) tabs.getChildren().collect(Collectors.toList()).get(maxPos);
tabs.getChildren().forEach( f ->{
int tabPos = Integer.valueOf(((Tab)f).getLabel());
if(tabs.indexOf(f)<maxPos) {
tabs.addComponentAtIndex( tabPos+1, f);
}
});
tabs.addComponentAtIndex(0, tab);
System.out.println(tabs.getChildren().collect(Collectors.toList()));
});
add(btn,ref,tabs);
}
}
When i'm reordering 3rd tab to 0 then my 1 tab is added after 2 tab. But when i'm looking into tabs order in getChildren then i can see that all tabs are in the correct order (3,0,1,2) and when i'm selecting tab 2 it is selecting both tab 1 and 2 and it is treating as if i would choose tab 1 (page for tab 1 is showing). I can't select tab 2 anymore.
I know that in this example i can use only tabs.addComponentAtIndex(0, tab); without reordering other tabs but i need it in my reordering mechanism which is more complicated but problem is this same. In selection listener when i select tab 2 it treats it if i had choose tab 1.
Here it is the order of tabs i getChildren aftek reordering:
[Tab{3}, Tab{0}, Tab{1}, Tab{2}]
It is working fine if remove all tabs and when i add them again in correct order.
Content in the element of the component looks correct as well:
<vaadin-tabs>
<vaadin-tab>
3
</vaadin-tab>
<vaadin-tab>
0
</vaadin-tab>
<vaadin-tab>
1
</vaadin-tab>
<vaadin-tab>
2
</vaadin-tab>
</vaadin-tabs>
For 7 tabs it is even weirder when i would change 6 tabs to 0:

Related

How to undo changes made from script on contenteditable div

Let's have contenteditable div. Browser itself manage undo on it.
But when additional content changes (or touching selection ranges) are made from script (in addition to user action) then it stops behave as user expected.
In other words when user hit Ctrl+Z then div content is not reverted to previous state.
See following simplified artificial example:
https://codepen.io/farin/pen/WNEMVEB
const editor = document.getElementById("editor")
editor.addEventListener("keydown", ev => {
if (ev.key === 'a') {
const sel = window.getSelection()
const range = window.getSelection().getRangeAt(0)
const node = range.startContainer;
const value = node.nodeValue
node.nodeValue = value + 'aa'
range.setStart(node, value.length + 2)
range.setEnd(node, value.length + 2)
ev.preventDefault()
}
})
All written 'a' letters are doubled.
Undo is ok as long as there is no 'a' typed.
When user typed 'a' (appended to text as double 'aa') and hits Ctrl+Z, then he expects both 'a' will be removed and cursor moves back to original position.
Instead only one 'a' is reverted on undo and second one added by script remain.
If event is also prevented by preventDefault() (which is not needed in this example, but in my real world example i can hardly avoid it) then all is worse.
Because undo reverts previous user action.
I could images that whole undo/redo stuff will be managed by script, but it means implementation of whole undo/redo logic. That's too complicated, possible fragile and with possible many glitches.
Instead I would like tell browser something like that there is atomic change which should be reverted by one user undo. Is this possible?
You can store the "revisions" in an array, then push the innerHTML of the div to it whenever you programmatically change the innerHTML of it.
Then, you can set the innerHTML of the div to the last item in the revisions array whenever the user uses the Ctrl + Z shortcut.
const previousRevisions = []
function saveState() {
previousRevisions.push(editor.innerHTML)
}
function undoEdit() {
if (previousRevisions.length > 0) {
editor.innerHTML = previousRevisions.pop();
}
}
const editor = document.getElementById("editor")
editor.addEventListener("keydown", ev => {
if (ev.key === 'a') {
saveState()
const sel = window.getSelection()
const range = window.getSelection().getRangeAt(0)
const node = range.startContainer;
const value = node.nodeValue
node.nodeValue = value + 'a'
range.setStart(node, value.length + 1)
range.setEnd(node, value.length + 1)
} else if (ev.ctrlKey && ev.key == 'z') {
undoEdit()
}
})
#editor{width:600px;min-height:250px;border:1px solid black;font-size:24px;margin:0 auto;padding:10px;font-family:monospace;word-break:break-all}
<div id="editor" contenteditable="true">type here </div>
The benefit of this solution is that it will not conflict with the browser's native Ctrl + Z shortcut behavior.
Make the parent a div (if it isn't) and make it so it adds spans inside of it every time the user taps a so the new span and set it's id to span-keyword will have aa as the value / text. Then check if the users cursor is at the beginning of it and check if there is no other text in-front of it and the user did no other action in it. If there is no text and no other actions happened do this:
document.getElementById('span-keyword').remove();

How to provide multiple links in one cell of a QTableView

I have a QTableView in my project, in which several columns display data that includes a hyperlink. I use a delegate class for these to set it up so that when the cell in the column is clicked, it opens the linked page in the browser. This works great... when it's only one value being linked to one page. For example, I may have a list of search values for mysite.com where the columns have values A, B, C, etc.. If the user clicks on the cell in this column with A, it will open a hyperlink for mysite.com/A (again, this part works fine). However, I now need to add a column that may have something like "A,B", where it needs to support links to search for A AND B in the same cell depending on which they click. I've been searching around online for a while now and it seems like this probably can't be done with a delegate. I have a line in a QTextBrowser elsewhere in my code where I can do this via HTML, like this:
QString toShow;
for(int i = 0; i < searchValueList.size(); i++)
{
toShow.append("`<a href=\"www.mysite.com/" + searchValueList.at(i) + "\"`>" +
searchValueList.at(i) + "`</a`>";
}
However I can't find any way to set the cells in a QTableView to recognize HTML formatting or Rich Text, and alas I'm not even sure that's possible. Is there any way at all to do what I'm trying to accomplish?
You can create a custom QItemDelegate for the specific column in which you can display rich text. The delegate could be like :
class RichTextDelegate: public QItemDelegate
{
public:
RichTextDelegate(QObject *parent = 0);
void paint( QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index ) const;
};
RichTextDelegate::RichTextDelegate(QObject *parent):QItemDelegate(parent)
{
}
void RichTextDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
if( option.state & QStyle::State_Selected )
painter->fillRect( option.rect, option.palette.highlight() );
painter->save();
QTextDocument document;
document.setTextWidth(option.rect.width());
QVariant value = index.data(Qt::DisplayRole);
if (value.isValid() && !value.isNull())
{
document.setHtml(value.toString());
painter->translate(option.rect.topLeft());
document.drawContents(painter);
}
painter->restore();
}
You should set the item delegate for the specific column :
ui->tableView->setItemDelegateForColumn(colIndex, new RichTextDelegate(ui->tableView));
Now if you set the model text for the specific column in a row to a rich text, it will be shown properly :
model->item(rowIndex, colIndex)->setText(someRichText);

how to align a tab at the right in primefaces tabView?

I want to place a tab on the right (and leave the other on the left normal position) in the <p:tabView>... Like in the showcase site of primefaces (the tab of documentation)
You can achieve this with the jQuery children selector. If your tabView's id is form:acc, then the following would be correct:
var tabs = $( "#form\\:acc ul" ).children();
var selectedChild = tabs.eq(tabs.length - 1); //selects the last tab.
selectedChild.css( "float", "right" );
What this does is that it takes the last tab and moves it to the right. However, if you do not want the last tab to move, you can select it's index and your second line would change:
var selectedChild = tabs.eq(1); //selects the tab with index 1.

LayoutCycleDetection - Observablecollection insert twice

I have a strange exception. I get a "LayoutCycleException" when inserting an item into a ObservableCollection two times with a specific index.
Details: I created a Paging-Class, which handles a big ObservableCollection (up to 2000 items) into a single page (200 items). The user can then go to another page. This event causes the Pager to set the page items new. All works fine except one situation: Inserting new items into the page with specific index (0, 1, 2, ..) fires the exception after the second insert.
Anyone an idee why?
Code:
// Calculate the index for this page
var cp = (CurrentPage == 0) ? 1 : CurrentPage;
int pindex = index - ((cp - 1) * PageItemCount);
if (pindex >= 0 && pindex < PageItemCount)
{
// An item is inserted after the list is already loaded
if (pindex == 0)
{
// Check if the page has mor items than it should
if (_pagedItems.Count >= PageItemCount)
{
// Remove last item from page
_pagedItems.RemoveAt(_pagedItems.Count - 1);
}
_pagedItems.Insert(pindex, item);
}
else if (_pagedItems.Count < PageItemCount)
{
// --> EXCEPTION at third insert
_pagedItems.Insert(pindex, item);
}
}
EDIT (solution)
I found the bug in my code. The Problem was, that the UI had no time to update the collection-changed (UI was not responsive). Very strange bug, took me long time to find it...
A simple Thread.Sleep(10) changed the whole thing =) in this time the ui can change the collection and everything works fine.
The exception is not coming from ObservableCollection, but from an event generated when the layout is updated after InsertItem.
LayoutCycleDetection is typically generated when using the LayoutUpdated event handler. You can unintentionally create an infinite loop. In this case _pagedItems.Insert creates a LayoutUpdate event which may be calling _pagedItems to insert again.
If it's not found there, you may have a custom control calling LayoutUpdated and causing the exception.

content navigation help

I'm working on a content navigation in typoscript. These requirements should be accomplished:
show the current node at the top -> is done!
show all child nodes of the current page -> is done!
if there are no child nodes, show last treelevel with with the current page active - not done yet!
For the last point, I need help. I tried to do something with [treelevel = 2] to control the navigation on the last treelevel, but nothing happened. Don't know why but the treelevel-condition won't work for me. Another problem is, sometimes the last treelevel is on 2nd level, sometimes on 3rd...
any ideas?
This is the typoscript so far:
temp.leftCol = COA
temp.leftCol {
5 = HTML
5.value = <ul class="contentNav">
### show current page on top
10 = TEXT
10.typolink {
parameter.data = TSFE:id
}
10 {
wrap = <li class="title">|</li>
data = leveltitle:2
if {
isTrue.numRows {
table = pages
}
}
}
### content navigation: show subpages
20 = HMENU
20.entryLevel = -1
20.1 = TMENU
20.1 {
noBlur = 1
NO = 1
CUR = 1
expAll = 1
}
20.1.NO {
wrapItemAndSub = <li>|</li>
stdWrap.wrap = |
allStdWrap.insertData = 1
}
20.1.CUR {
wrapItemAndSub = <li class="on">|</li>
stdWrap.wrap = |
allStdWrap.insertData = 1
}
20.1.wrap = |</ul>
}
What do you mean by this exactly?
if there are no child nodes, show last treelevel with with the current page active - not done yet!
As I understand it, it could be either
1) Letssay that there are 10 pages on level 1, and every one of those pages except page 7 have 3 subpages each. And letssay page 7 has no subpages. Then, when I go to page 7, it will display page 7 as active, but display subpages from page 6 (the "show last treelevel") under it.
or
2) When user navigates to page 7, it displays page 7 as active, and subpages for whatever page the user has been right before that? So that for example, if I navigate to page 2 first, and then go to page 7, I will see page 7 as active, and subpages for page 2.