I have a DFM (is a Delphi source file, like JSON, to define form component layouts) parser created with javaCC.
My grammar (.jj file) define this:
private DfmObject dfmObject():
{
DfmObject res = new DfmObject();
DfmProperty prop;
DfmObject obj;
Token tName;
Token tType;
}
{
<OBJECT>
(tName = <IDENTIFIER> { res.setName(tName.image); } <COLON>)?
tType = <IDENTIFIER> { res.setType(tType.image); }
<ENDLINE>
( prop = property() { res.addProperty(prop); } )*
( obj = dfmObject() { res.addChild(obj); } (<ENDLINE>)*)*
<END>
{ return res; }
}
This is for parsing 2 types of object definitions:
object name: Type
end
as so
object Type
end
So, the name : is optional.
But, when I try to parse this second DFM, I always get this error:
Exception in thread "main" eu.kaszkowiak.jdfm.parser.ParseException: Encountered " <ENDLINE> "\r\n"" at line 1, column 12.
Was expecting:
":" ...
What I'm doing wrong?
A solution/workaround is, to make optional the : Type part and switch between the name and type values when the type == null.
See the grammar implementation:
private DfmObject dfmObject():
{
DfmObject res = new DfmObject();
DfmProperty prop;
DfmObject obj;
Token tName;
Token tType;
}
{
(
<OBJECT>
(
tName = <IDENTIFIER> { res.setName(tName.image); }
)
( <COLON> tType = <IDENTIFIER> { res.setType(tType.image); } )?
<ENDLINE>
)
( prop = property() { res.addProperty(prop); } )*
( obj = dfmObject() { res.addChild(obj); } (<ENDLINE>)*)*
<END>
{
if (res.getType() == null) {
res.setType(res.getName());
res.setName(null);
}
return res;
}
}
I'm trying to create an API, which can dynamically populate tableview with different Nested UI components in each cell of tableview. I'm able to bind the data set to Model object with the table.
The problem is when I try to dynamically add and try to enable the edit, the object reference is seems to messed up.
FYI:
As you see I've last column with 4 buttons, which is Add, Edit, Delete, Reset features. Once click on Add - it clones the current row, click on edit - it enable the ComboBox of Coulmn Category, click on delete - it deletes current row.
What I face is that upon adding multiple entries, I do get the row added dynamically, but then click on first row edit button - then multiple ComboBox is enabled, which is not intended use. The use case is current row's ComboBox must be only be enabled.
Implementation: I've written custom API which extends TableView<S>.
Following snippet may help:
//column category
final ClumpElement< ConstraintsDataModel, String > categoryElement =
new ClumpElement<>( ClumpType.COMBOBOX, true, getCategoryData() );
categoryElement.setClumpTableCellValue( data -> data.categoryProperty() );
categoryElement.setClumpTableNodeAction( ( control, data ) -> {
final ComboBox< String > comboBox = (ComboBox< String >)control;
comboBox.disableProperty().bind( data.disableProperty() );
} );
clumpTableView.addNewColumn( "Category", categoryElement );
// column Action
final ClumpElement< ConstraintsDataModel, String > buttonsElement =
new ClumpElement<>( ClumpType.GROUP_BUTTONS, 4, "+", "✎", "X", "↻" );
buttonsElement.setClumpTableNodeAction( ( control, data ) -> {
final Button button = (Button)control;
switch( button.getText() ) {
case "+":
final ConstraintsDataModel ref =
clumpTableView.getItems().get( clumpTableView.getItems().size() - 1 );
if( ConstraintsDataModel.isValidModel( ref ) )
clumpTableView.getItems().add( new ConstraintsDataModel( data ) );
else
System.out.println( "ERROR: Finish previous constraints" );
break;
case "✎":
data.setDisableValue( false );
button.setText( "✔" );
break;
case "✔":
data.setDisableValue( true );
button.setText( "✎" );
break;
default:
//NOTHING
break;
}
} );
clumpTableView.addNewColumn( "Action", buttonsElement );
clumpTableView.setItems( getData() );
This is my CustomTableView class:
public < T > void addNewColumn( final String columnName, final ClumpElement< S, T > element ) {
final TableColumn< S, T > column = new TableColumn<>( columnName );
getColumns().add( column );
if( element.getClumpTableCellValue() != null ) {
column.setCellValueFactory( param -> element.getClumpTableCellValue()
.act( param.getValue() ) );
}
clumpCellCall( columnName, element, column );
}
private < T > void clumpCellCall( final String colName, final ClumpElement< S, T > element,
final TableColumn< S, T > column ) {
switch( element.getUiNode() ) {
case COMBOBOX:
if( element.getItems() != null && !element.getItems().isEmpty() ) {
column.setCellFactory( param -> {
final ClumpComboBoxTableCell< S, T > clumpComboBoxTableCell =
new ClumpComboBoxTableCell<>( element.isDisable(), element.getItems() );
clumpComboBoxTableCell.prefWidthProperty().bind( column.widthProperty() );
clumpComboBoxTableCell.selectionListener( element );
return clumpComboBoxTableCell;
} );
}
break;
case GROUP_BUTTONS:
column.setCellFactory( param -> {
final ClumpButtonsTableCell< S, T > clumpButtonsTableCell =
new ClumpButtonsTableCell<>( element.getNoOfElements() );
clumpButtonsTableCell.prefWidthProperty().bind( column.widthProperty() );
IntStream.range( 0, element.getNoOfElements() ).forEach( item -> {
final Button button = clumpButtonsTableCell.getButtons().get( item );
button.setText( element.getNames().get( item ) );
button.setOnAction( event -> {
if( element.getClumpTableNodeAction() != null
&& clumpButtonsTableCell.getIndex() < getItems().size() ) {
element.getClumpTableNodeAction()
.act( button, getItems().get( clumpButtonsTableCell.getIndex() ) );
}
} );
} );
return clumpButtonsTableCell;
} );
break;
default:
column.setCellFactory( params -> {
final TextFieldTableCell< S, T > textFieldTableCell = new TextFieldTableCell<>();
textFieldTableCell.setConverter( new StringConverter< T >() {
#Override
public String toString( final T object ) {
return (String)object;
}
#Override
public T fromString( final String string ) {
return (T)string;
}
} );
return textFieldTableCell;
} );
break;
}
}
In my custom API, which shall invoke a custom TableCell<S,T> which has ComboBox<T> pretty standard implementation as per docs. Here its inside a selection listener, as I found that when the cell renders, only this selection listener is called.
public abstract class AbstractClumpTableCell< S, T > extends TableCell< S, T > {
public AbstractClumpTableCell() {
setContentDisplay( ContentDisplay.GRAPHIC_ONLY );
setAlignment(Pos.CENTER);
}
public abstract void renewItem( T item );
#Override
protected void updateItem( T item, boolean empty ) {
super.updateItem( item, empty );
if( empty ) {
setGraphic( null );
} else {
renewItem( item );
}
}
}
public class ClumpComboBoxTableCell< S, T > extends AbstractClumpTableCell< S, T > {
private final ComboBox< T > comboBox;
#SuppressWarnings( "unchecked" )
public ClumpComboBoxTableCell( final boolean isDisable, final ObservableList< T > item ) {
super();
this.comboBox = new ComboBox<>( item );
this.comboBox.setDisable( isDisable );
this.comboBox.valueProperty().addListener( ( obs, oVal, nVal ) -> {
ObservableValue< T > property = getTableColumn().getCellObservableValue( getIndex() );
if( property instanceof WritableValue ) {
((WritableValue< T >)property).setValue( nVal );
}
} );
}
#Override
public void renewItem( T item ) {
comboBox.setValue( item );
setGraphic( comboBox );
}
public ComboBox< T > getComboBox() {
return comboBox;
}
protected void selectionListener( final ClumpElement< S, T > element ) {
this.comboBox.getSelectionModel().selectedItemProperty().addListener( ( obs, oVal, nVal ) -> {
if( element.getClumpTableNodeAction() != null
&& getIndex() < getTableView().getItems().size() ) {
element.getClumpTableNodeAction().act( this.comboBox,
getTableView().getItems().get( getIndex() ) );
}
} );
}
}
And my Data Model has a SimpleStringProperty that is binded to the column accordingly.
So, How can I bind the Nested UI elements correctly rowise within the TableView<S>? Is my approach right or is there alternatives?
I will make an attempt to answer, but as I said the code is hard for me to follow (especially as it is partial, so some methods I can only assume the purpose of).
The issue, as stated in the comments, is node virtualization in TableView. You can't go around it, and you really don't want to - it is a means to vastly improve performance, as you don't need hundreds or thousands of UI nodes (which are "heavy" and degrade performance), but only enough to fill the displayed portion of the table, thus supporting a much larger dataset.
The problem, as far as I can see, as that you have some property of the row (is it currently editable or not) which you need to be reflected in certain columns. More specifically, you want the combo box's disable property to always reflect the disable property of the row it pertains to, so in updateItem you will have to do something like this:
#Override
protected void updateItem(T item, boolean empty) {
super.updateItem(T, empty);
if (empty) {
setGraphic(null);
} else {
renewItem(item);
// since the disable property if given by the row value, not only the column value
// we need to get the row value. The cast is needed due to a design oversight
// in JavaFX 8, which is fixed in newer versions. See https://bugs.openjdk.java.net/browse/JDK-8144088
ConstraintsDataModel data = ((TableRow<ConstraintsDataModel>)getTableRow())
.getItem();
combobox.disableProperty().unbind();
combobox.disableProperty().bind(data.disableProperty());
}
}
This is assuming your row data type is indeed ConstaintDataModel, I couldn't quite follow through.
Another option which may be more elegant is to use the editing property of a row - bind the combo box's disable property to the negation of the editing property of the row, and use startEdit and cancelEdit/commitEdit when you start and end editing. This way you will not have to re-bind the disable property of the combo box, as it will always refer to the correct row.
The regular focus traversal keys are TAB for moving focus forward and SHIFT+TAB for moving focus backward. And now I want to use ENTER key instead of TAB. Are there any idea for this?
Qinn
This is how I have done it:
rootNode.addEventFilter( KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>()
{
Robot eventRobot = new Robot();
#Override
public void handle( KeyEvent KV )
{
switch ( KV.getCode() )
{
case ENTER :
{
if ( ! (KV.getTarget() instanceof TextArea) )
{
eventRobot.keyPress( java.awt.event.KeyEvent.VK_TAB );
eventRobot.keyRelease( java.awt.event.KeyEvent.VK_TAB );
KV.consume();
}
break;
}
case TAB :
{
if ( ! (KV.getTarget() instanceof TextArea) )
{
KV.consume();
}
break;
}
}
}
}
I Think it would be better off if you use two classes:
KeyCombination for a more precise key checking along with
KeyboardShortcutsHandler to move focus.
So there's no need to use a Robot to send tab key
final KeyCombination ENTER = new KeyCodeCombination(KeyCode.ENTER);
rootNode.addEventFilter(KeyEvent.KEY_PRESSED, (KeyEvent KV) -> {
EventTarget tgt = KV.getTarget();
if (ENTER.match(KV)) {
KeyboardShortcutsHandler ksh = new KeyboardShortcutsHandler();
ksh.traverse((Node) tgt, Direction.NEXT);
KV.consume();
} else if ( ! (KV.getTarget() instanceof TextArea) ) {
KV.consume();
}
});
How do I get this $_GET['ago'] variable to increment by 1
public function __construct($future=false) {
$GLOBALS['twd_helper']=&$this;
}
public function query_string($add, $remove=null) {
$qs_data = array();
parse_str($_SERVER['QUERY_STRING'], $qs_data);
$qs_data = array_merge($qs_data, $add);
if ($remove) {
foreach($remove as $key) {
if (isset($qs_data[$key])) {
unset($qs_data[$key]);
}
}
}
return http_build_query($qs_data);
}
public function filter_overnight () {
if (isset($_GET['tod']) && $_GET['tod'] == 'overnight') {
$overnight = $_GET['overnight'];
}
if (($_GET['ago'])) {
$_GET['ago'] ? $_GET['ago']++ : $_GET['ago'] = 0;
}
}
I want to change my URL to increase by 1 each time its clicked
guide?ago=1
guide?ago=2
guide?ago=3
etc
In the html, just do this:
Link Text
Your filter_overnight function is a bit odd too - I think it could be made more readable:
if ($_GET['ago'])
{
$_GET['ago'] = (int)$_GET['ago'] + 1;
}
else
{
$_GET['ago'] = 0;
}
Or with the ternary:
$_GET['ago'] = ($_GET['ago']) ? (int)$_GET['ago'] + 1 : 0
Without seeing the whole code, I'm not sure why filter_overnight is incrementing $_GET['ago'], but at a glance it appears to put an undocumented side effect into the function by changing a global value (which would also render the increment in the <a> pointless). The superglobals in PHP can be dangerous things - I find it best to treat them as immutable and copy values out of them vs working on them directly.
What's the best way to implement a classic curry function in actionscript with a nice syntax?
I've tried:
Function.prototype.curry = function()
{
return "helloWorld";
}
trace((function():void {}).curry());
...approach but that didn't work.
I guess I'm stuck with a ugly approach such as:
FunctionUtils.curry(fp, ... args)
???
I must admit I've never understood the difference between "curry" and "partial". I use the following function to do more or less what you want to do:
package {
public function partial( func : Function, ...boundArgs ) : Function {
return function( ...dynamicArgs ) : * {
return func.apply(null, boundArgs.concat(dynamicArgs))
}
}
}
Usage examples:
var multiply : Function = function( a : Number, b : Number ) : Number { return a * b; }
var multiplyByFour : Function = partial(multiply, 4);
trace(multiplyByFour(3)); // => 12
Ended up with (heavily inspired by dojo's implementation):
public static function curry(func:Function, ... args:Array):*
{
var arity:int = func.length;
var currying:Function = function(func:Function, arity:int, args:Array):*
{
return function(... moreArgs:Array):* {
if(moreArgs.length + args.length < arity)
{
return currying(func, arity, args.concat(moreArgs));
}
return func.apply(this, args.concat(moreArgs));
}
}
return currying(func, arity, args);
}
Request in the comments section to show an example of how to use this:
function foo(i:int, j:int):void
{
trace(i+j);
}
function bar(fp:Function):void
{
fp(2);
}
bar(FunctionUtils.curry(foo, 1)); //trace==3
Silly example, I know, but curry:ing is extremely useful. Have a look at http://www.svendtofte.com/code/curried_javascript/ for theory.