The requirement: show the coordinates of the mouseEvent that triggered the tooltip as its text. For a contextMenu, the location is stored in the contextMenuEvent, so I would listen to contextMenuRequested and update as needed.
Couldn't find anything similar for a tooltip, so played a bit (see example below):
at the time of showing/shown, I could query the tooltip location: for AnchorLocation.CONTENT_TOP_LEFT its x/y seems to be about the last mouse location, though slightly increased. Could be accidental, though, is unspecified (and as such unusable) and definitely off for other anchor types
the brute force method would be to install a mouse-moved handler and store the current mouse location into the tooltip's properties. Wouldn't like to, because that's duplicating functionality, as ToolTipBehaviour already keeps track of the triggering location, unfortunately top secretly, as usual
extending tooltip wouldn't help as well, due to the private scope of the behaviour
Any ideas?
public class DynamicTooltipMouseLocation extends Application {
protected Button createButton(AnchorLocation location) {
Tooltip t = new Tooltip("");
String text = location != null ? location.toString()
: t.getAnchorLocation().toString() + " (default)";
if (location != null) {
t.setAnchorLocation(location);
}
t.setOnShown(e -> {
// here we get a stable tooltip
t.textProperty().set("x/y: " + t.getX() + "/" + t.getY() + "\n" +
"ax/y: " + t.getAnchorX() + "/" + t.getAnchorY());
});
Button button = new Button(text);
button.setTooltip(t);
button.setOnContextMenuRequested(e -> {
LOG.info("context: " + text + "\n " +
"scene/screen/source " + e.getSceneX() + " / " + e.getScreenX() + " / " + e.getX());
});
button.setOnMouseMoved(e -> {
LOG.info("moved: " + text + "\n " +
"scene/screen/source " + e.getSceneX() + " / " + e.getScreenX() + " / " + e.getX());
});
return button;
}
#Override
public void start(Stage stage) throws Exception {
VBox pane = new VBox(createButton(AnchorLocation.CONTENT_TOP_LEFT));
Scene scene = new Scene(pane);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
#SuppressWarnings("unused")
private static final Logger LOG = Logger
.getLogger(DynamicTooltipMouseLocation.class.getName());
}
I'm not sure if I've understood your question right, but if you are looking for the screen coordinates of the mouse, right at the position when the tooltip is shown, I think you almost got them.
You have already looked at Tooltip class and its inner class TooltipBehavior.
For starters there are these hardcoded offsets:
private static int TOOLTIP_XOFFSET = 10;
private static int TOOLTIP_YOFFSET = 7;
Then, in the inner class a mouse moved handler is added to the node, tracking the mouse in screen coordinates, and showing the tooltip based on several timers:
t.show(owner, event.getScreenX()+TOOLTIP_XOFFSET,
event.getScreenY()+TOOLTIP_YOFFSET);
Given that it uses this show method:
public void show(Window ownerWindow, double anchorX, double anchorY)
The coordinates you are looking for are just these:
coordMouseX=t.getAnchorX()-TOOLTIP_XOFFSET;
coordMouseY=t.getAnchorY()-TOOLTIP_YOFFSET;
no matter how the tooltip anchor location is set.
I've checked this also in your answer to the question, and these values are the same as the Point2D screen you set to the tooltip.
Anyway, since this solution uses hardcoded fields from private API, I assume you won't like it, since those can change without notice...
Related
I made a simple java program to move the mouse cursor 1 pixel every 10 seconds. The program works as its supposed to when compiled and ran from command line. But when running the program from the .jar the cursor doesnt move. I know the timer is still running because I have that output to the JFrame. Any ideas on why the timer still runs but the cursor doesnt move like it does when ran from command line?
public static void main(String args[]) {
JFrame frame = new JFrame("Mouse-Mover");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(600, 300);
JButton startButton = new JButton("Start");
frame.add(startButton, BorderLayout.NORTH);
JButton stopButton = new JButton("Stop");
frame.add(stopButton, BorderLayout.SOUTH);
frame.setVisible(true);
//when start button is clicked, start the timer
startButton.addActionListener(e -> {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
#Override
public void run() {
int cursorPositionX = MouseInfo.getPointerInfo().getLocation().x;
int cursorPositionY = MouseInfo.getPointerInfo().getLocation().y;
int firstX = cursorPositionX + 1;
int firstY = cursorPositionY + 1;
try {
Robot robot = new Robot();
robot.mouseMove(firstX, firstY);
} catch (AWTException e) {
e.printStackTrace();
}
System.out.println("Running: " + new java.util.Date());
JTextArea jTextArea = new JTextArea();
jTextArea.setEditable(false);
jTextArea.setText("Running: " + "X Coordinate: "+firstX + " " + "Y Coordinate: " + firstY + " " + new java.util.Date());
frame.add(jTextArea, BorderLayout.CENTER);
frame.add(jTextArea);
frame.setVisible(true);
stopButton.addActionListener(e -> timer.cancel());
//system.out.print size of jframe
System.out.println(frame.getSize());
}
}, 0, 1000);
});}}
`
A .jar is a compiled application.
Below is an example runnable of how this can be done. Do keep in mind however, as already stated within comments, this may not work properly or (not work at all) on some platforms due to security reasons. Some platforms may not allow for access to low-level input control. You would need to set special privileges or extensions in the OS for your application to function properly.
Please read the comments in code:
package movemouseeveryseconddemo;
public class MoveMouseEverySecondDemo {
public int interval = 1000;
public int pixelMovementX = 1;
public int pixelMovementY = 1;
public javax.swing.Timer moveTimer;
public java.awt.Robot robot;
public final javax.swing.JFrame frame = new javax.swing.JFrame("Mouse-Mover");
public final javax.swing.JTextArea jTextArea = new javax.swing.JTextArea();
public int firstX = 0, firstY = 0;
public MoveMouseEverySecondDemo() {
try {
// Initialize robot
robot = new java.awt.Robot();
}
/* Robot may not work in some operating systems due to improper
permissions. If this is the case then OS configurations will
need to be done to allow Robot to function. */
catch (SecurityException ex) {
System.out.println("Permission required for Robot or this application"
+ "to function within Operating System!");
}
/* Just as the Exception Description states. Operating System
Configuration will need to be done in order for this application
to perform low-level input control such as what Robot does. */
catch (java.awt.AWTException ex) {
System.out.println("The Operating System configuration does not "
+ "allow low-level input control!");
}
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
new MoveMouseEverySecondDemo().startApp(args);
}
private void startApp(String[] args) {
/* See if there are Command-Line arguments...
Allows for the User to supply a desired Timer Interval and desired pixel
X/Y movement amounts. The interval must be in Milliseconds (default is
1000 [1 second]) and the pixel movement must be an integer value (default
is 1). Any or no Command-Line arguments can be supplied and they can be
supplied in any order. */
if (args.length > 0) {
for (String arg : args) {
String value = "";
String cma = arg.contains(":") ? arg.split(":")[0].toLowerCase() : arg.toLowerCase();
switch (cma) {
case "-help":
case "/help":
case "/?":
case "-?":
displayHelp();
System.out.println();
System.exit(0);
break;
case "/i":
case "-i":
value = arg.split(":")[1].trim();
if (!value.matches("\\d+")) {
System.out.println("Invalid value (" + value + ") supplied to the /I:[n] command-line parameter!");
System.out.println("Ignoring this argument and default of " + this.interval + " will be used!");
break;
}
this.interval = Integer.parseInt(value);
break;
case "/x":
case "-x":
value = arg.split(":")[1].trim();
if (!value.matches("\\d+")) {
System.out.println("Invalid value (" + value + ") supplied to the /X:[n] command-line parameter!");
System.out.println("Ignoring this argument and default of " + this.pixelMovementX + " will be used!");
break;
}
this.pixelMovementX = Integer.parseInt(value);
break;
case "/y":
case "-y":
value = arg.split(":")[1].trim();
if (!value.matches("\\d+")) {
System.out.println("Invalid value (" + value + ") supplied to the /Y:[n] command-line parameter!");
System.out.println("Ignoring this argument and default of " + this.pixelMovementY + " will be used!");
break;
}
this.pixelMovementY = Integer.parseInt(value);
break;
default:
System.out.println("Invalid command-line argument supplied! (" + arg + ")");
System.out.println("This argument will be ignored!");
}
}
}
// Initialize the timer.
moveTimer = new javax.swing.Timer(interval, new moveTimerActionListener());
// Set some JFrame properties
frame.setAlwaysOnTop(true);
frame.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
frame.setSize(600, 300);
/* Set up a Window Listener for JFrame to detect when it's
closing so that the Timer can stopped if it's running
and, return an application Exit indicator for the Console. */
frame.addWindowListener(new java.awt.event.WindowAdapter() {
#Override
public void windowClosing(java.awt.event.WindowEvent e) {
// If the timmer is running then stop it.
if (moveTimer.isRunning()) {
moveTimer.stop();
System.out.printf("%-13s | %-25s%n", "Timer Stopped", getCurrentDateTime());
System.out.println("Timer Stopped!");
}
System.out.println("Application Closed!");
// Shut down application.
System.exit(0);
}
});
// Create the Start button and add to JFrame
javax.swing.JButton startButton = new javax.swing.JButton("Start");
frame.add(startButton, java.awt.BorderLayout.NORTH);
// Set some JTextArea properties and add JTextArea to JFrame
jTextArea.setEditable(false);
jTextArea.setText("NOT RUNNING: Mouse X: 0 - Mouse Y: 0 | Date-Time: "
+ getCurrentDateTime());
frame.add(jTextArea, java.awt.BorderLayout.CENTER);
// Create the Stop button and add to JFrame
javax.swing.JButton stopButton = new javax.swing.JButton("Stop");
frame.add(stopButton, java.awt.BorderLayout.SOUTH);
// Set the JFrame to center Screen and make it visible.
frame.setLocationRelativeTo(null);
frame.setVisible(true);
/* The Start Button ActionPerformed event. When the `Start`
button is selected, the timer starts. */
startButton.addActionListener(e -> {
/* If User tries to Start the Timer while it's already
running then prevent it and indicate `No Can Do` in
the Console and JTextArea. */
if (moveTimer.isRunning()) {
String curDateTime = getCurrentDateTime();
jTextArea.setText("Failed Start! Timer is still running! " + curDateTime);
System.out.printf("%-13s | %-25s%n", "Failed Start", curDateTime);
System.out.println("-------------------------------");
System.out.println("-- Failed To Restart! --");
System.out.println("-- Timer is still running! --");
System.out.println("-- Press STOP Button! --");
System.out.println("-------------------------------");
}
// Otherwise, indicate the timer has started.
else {
moveTimer.start();
System.out.println("--------------------");
System.out.println("-- Timer Started! --");
System.out.println("--------------------");
// Header Line in console for output data.
System.out.println("--------------------------------------------"
+ "--------------------------------------------");
System.out.println(" Status Date - Time "
+ " Form Dimensions Mouse Coordinates");
System.out.println("--------------------------------------------"
+ "--------------------------------------------");
}
});
/* The Stop Button ActionPerformed Event. When stop
button is selected, the timer Stops. */
stopButton.addActionListener(e -> {
// If the `moveTimer` Timer is not running then get otta here.
if (!moveTimer.isRunning()) {
return;
}
moveTimer.stop(); // Stop the timer.
String curDateTime = getCurrentDateTime();
// Place the 'Stopped' fact into the JTextArea.
jTextArea.setText("NOT RUNNING | STOPPED: Mouse X: " + firstX
+ " - " + "Mouse Y: " + firstY + " | Date-Time: " + curDateTime);
// Also place the 'Stopped' fact into the Console Window.
System.out.printf("%-13s | %-25s%n", "Stopped", curDateTime);
System.out.println("--------------------");
System.out.println("-- Timer Stopped! --");
System.out.println("--------------------");
});
}
public String getCurrentDateTime() {
/* Establish the current Date-Time using methods from the java.time package.
These methods a better than the antique & troublesome Date class. */
return java.time.LocalDate.now() + " - " + java.time.LocalTime.now();
}
private void displayHelp() {
System.out.println();
System.out.println("MoveMouseEverySecondDemo Command-Line Help:");
System.out.println("Command-Line Options:");
System.out.println("---------------------");
System.out.println(" /? or -? or /help or -help - This help information.");
System.out.println(" /I:[value] or -I:[value] - The interval at which the internal timer");
System.out.println(" should fire. The value provided must be in");
System.out.println(" milliseconds. Default is: 1000.");
System.out.println(" /X:[value] or -X:[value] - The desired amount of X axis (horizontal)");
System.out.println(" mouse pointer movement for every fired");
System.out.println(" interval. Default is 1.");
System.out.println(" /Y:[value] or -Y:[value] - The desired amount of Y axis (vertical)");
System.out.println(" mouse pointer movement for every fired");
System.out.println(" interval. Default is 1.");
}
/* A Inner-Class: The Action Listener for the Timer. On every timer interval,
the ActioPerformed Event is fired within this inner-class. */
class moveTimerActionListener implements java.awt.event.ActionListener {
#Override
public void actionPerformed(java.awt.event.ActionEvent e) {
// Establish current date-time for output.
String curDateTime = getCurrentDateTime();
// Get the current Mouse Pointer `X` location on the Screen.
int cursorPositionX = java.awt.MouseInfo.getPointerInfo().getLocation().x;
// Get the current Mouse Pointer `Y` location on the Screen.
int cursorPositionY = java.awt.MouseInfo.getPointerInfo().getLocation().y;
/* Apply the `X` location plus the desired movement amount
to the MoveMouseEverySecondDemo class `firstX` member
variable. */
firstX = cursorPositionX + pixelMovementX;
/* Apply the `Y` location plus the desired movement amount
to the MoveMouseEverySecondDemo class `firstY` member
variable. */
firstY = cursorPositionY + pixelMovementY;
// Let Robot move the mouse pointer to the new screen location.
robot.mouseMove(firstX, firstY);
// Update the information within the JTextArea.
jTextArea.setText("RUNNING: " + "Mouse X: " + firstX + " - "
+ "Mouse Y: " + firstY + " | Date-Time: " + curDateTime);
// Also update the information to Console Window:
System.out.printf("%-13s | %-25s | %-20s | %-22s%n", "Timer Running", curDateTime,
"Form Size: " + frame.getWidth() + ", " + frame.getHeight(),
"Mouse X: " + firstX + ", Y: " + firstY);
}
}
}
This runnable application will run if started from the IDE or if compiled to a JAR file and started from a Command/Console Window. The provided demo application above does of course allow for Command-Line arguments so to change some operational features. They are as follows:
/? or -? or /help or -help:
Displays help about the Command-Line arguments. Application closes after displaying.
/I:[value] or -I:[value]:
The Interval at which the internal timer should fire. The value provided must be in milliseconds. Default is: 1000 (1 second).
/X:[value] or -X:[value]:
The desired amount of X axis (horizontal) mouse pointer movement for every fired interval. Default is 1 (DPI or Pixel if you have a real good mouse with a high resolution setting).
/Y:[value] or -Y:[value]:
The desired amount of Y axis (vertical) mouse pointer movement for every fired interval. Default is 1 (DPI or Pixel if you have a real good mouse with a high resolution setting).
Any number of command-line arguments can be supplied and they can be supplied in any order. Letter case is also ignored. Any Invalid argument is ignored and its related default will be used.
This demo application will provide information in both the GUI or the Console Window. You choose what you want to keep.
I'm writing a folder synchronization app. Currently, I'm working on the part responsible for recursively going through the two user-specified directory structures, comparing the folders and files in them to the other structure, and displaying whether each file or folder is unchanged, changed, or new, by means of a colored dot. The problem is that the program in its current state, while evaluating the relation correctly, only displays the dot on one TreeItem per dot color instead on all of them. Pic for reference:
Whats causing this? I have a suspicion that it has to do with the way object assignment works in Java, so I'm reassigning one and the same object somehow to all the proper TreeItems, only stopping at the last one, but that's too broad to work with. See the offending function below.
private void compareAndFillInSourceTreeView(Path x, TreeItem root) throws IOException {
String xSourceName = x.getName(x.getNameCount() - 1).toString();
String xTargetName = (getEquivalentFileInTarget(x).getName(getEquivalentFileInTarget(x).getNameCount() - 1))
.toString();
System.out.println("-----------------------------------------------------------------------------------------");
System.out.println("NEW CALL: " + x.toString() + " " + root);
System.out.println("EQUIVALENT: " + getEquivalentFileInTarget(x) + " EXISTS: " +
getEquivalentFileInTarget(x).toFile().exists());
System.out.println("IS NEW: " + xTargetName + ", " + (xTargetName == null));
System.out.println("UNCHANGED: " + x + " " + getEquivalentFileInTarget(x) + " NAMES: " + xSourceName + ", "
+ xTargetName);
System.out.println("CHANGED: " + ((x.getName(x.getNameCount() - 1)) ==
getEquivalentFileInTarget(x).getName(getEquivalentFileInTarget(x).getNameCount() - 1)));
if (x.toFile().isFile()) {
System.out.println("THIS IS A FILE: " + x.toString());
//if new, i.e. doesn't exist in the target
if (!getEquivalentFileInTarget(x).toFile().exists()) {
System.out.println("EQUIVALENT DOESN'T EXIST FOR THIS FILE IN TARGET");
TreeItem newBranch = makeBranch(xSourceName, root);
newBranch.setGraphic(blueDotIcon);
}
//if unchanged
else if (sameContents(x, getEquivalentFileInTarget(x)) && (xSourceName.equals(xTargetName))) {
System.out.println("THIS FILE AND ITS EQUIVALENT ARE EQUAL");
TreeItem newBranch = makeBranch(x.getName(x.getNameCount() - 1).toString(), root);
newBranch.setGraphic(greenDotIcon);
}
//if same name, but different contents, i.e. changed
else if ((x.getName(x.getNameCount() - 1)).equals(
getEquivalentFileInTarget(x).getName(getEquivalentFileInTarget(x).getNameCount() - 1))) {
TreeItem newBranch = makeBranch(x.getName(x.getNameCount() - 1).toString(), root);
newBranch.setGraphic(yellowDotIcon);
} else {
System.out.println("BAD, putInTreeView() Error, it should never reach this line");
System.out.println("Error log: " + x + ", " + getEquivalentFileInTarget(x));
}
} else if (x.toFile().isDirectory()){ //if it's a folder, checked explicitly because it's behaving weird
System.out.println("THIS IS A DIRECTORY: " + x.toString());
if (getEquivalentFileInTarget(x).toFile().exists()) {
System.out.println("EQUIVALENT EXISTS FOR THIS DIRECTORY IN TARGET.");
//make new branches and mark them as existing folders
TreeItem currentSourceTreeViewRoot = makeBranch(x.getName(x.getNameCount() - 1).toString(), root);
currentSourceTreeViewRoot.setExpanded(true);
currentSourceTreeViewRoot.setGraphic(greenDotIcon);
for (File i : x.toFile().listFiles()) {
System.out.println("Rec. called for: " + currentSourceTreeViewRoot);
compareAndFillInSourceTreeView(i.toPath(), currentSourceTreeViewRoot);
}
} else {
System.out.println("EQUIVALENT DOESN'T EXIST FOR THIS DIRECTORY IN TARGET.");
//if they don't exist, make the branches anyway and mark them as representing nonexistent folders
TreeItem currentSourceTreeViewRoot = makeBranch((x.getName(x.getNameCount() - 1)).toString(), root);
currentSourceTreeViewRoot.setExpanded(true);
for (File i : x.toFile().listFiles()) {
System.out.println("Rec. called for: " + currentSourceTreeViewRoot);
compareAndFillInSourceTreeView(i.toPath(), currentSourceTreeViewRoot);
}
}
}
}
Your assumption is correct. When assigning a graphic with setGraphic you are telling JavaFX where in the scene graph to locate this node. When you call setGraphic again with the same object as parameter you are effectively moving it to a different place in the scene graph.
Create a new dot/circle for every item and your problem should be solved.
UPDATE 3
Final working code below. YOU NEED THE ace.js FROM THE src FOLDER! It will not work from the libs, you need the pre-packaged version from their site.
WText *editor = new WText(root());
editor->setText("function(){\n hello.abc();\n}\n");
editor->setInline(false);
The above code can set the contents of the ACE window.
MyClass::MyClass(const WEnvironment& env)
: WApplication(env)
{
wApp->require("ace-builds/src/ace.js");
// A WContainerWidget is rendered as a div
WContainerWidget *editor = new WContainerWidget(root());
editor->resize(500, 500);
std::string editor_ref = editor->jsRef(); // is a text string that will be the element when executed in JS
std::string command =
editor_ref + ".editor = ace.edit(" + editor_ref + ");" +
editor_ref + ".editor.setTheme(\"ace/theme/monokai\");" +
editor_ref + ".editor.getSession().setMode(\"ace/mode/javascript\");";
editor->doJavaScript(command);
JSignal <std::string> *jsignal = new JSignal<std::string>(editor, "textChanged");
jsignal->connect(this, &MyClass::textChanged);
WPushButton *b = new WPushButton("Save", root());
command = "function(object, event) {" +
jsignal->createCall(editor_ref + ".editor.getValue()") +
";}";
b->clicked().connect(command);
}
void MyClass::textChanged(std::string incoming)
{
}
UPDATE 2
Here is what my project looks like atm, still getting a white screen with a red "Loading..." message from WT in the top right hand corner. More notes below.
MyClass::MyClass(const WEnvironment& env)
: WApplication(env)
{
wApp->require("lib/ace/ace.js");
// A WContainerWidget is rendered as a div
WContainerWidget *editor = new WContainerWidget(root());
editor->resize(500, 500);
std::string editor_ref = editor->jsRef(); // is a text string that will be the element when executed in JS
std::string command =
editor_ref + ".editor = ace.edit(" + editor_ref + ");" +
editor_ref + ".editor.setTheme(\"ace/theme/monokai\");" +
editor_ref + ".editor.getSession().setMode(\"ace/mode/javascript\");";
editor->doJavaScript(command);
JSignal <std::string> *jsignal = new JSignal<std::string>(editor, "textChanged");
jsignal->connect(this, &MyClass::textChanged);
WPushButton *b = new WPushButton("Save", root());
command = "function(object, event) {" +
jsignal->createCall(editor_ref + ".editor.getValue()") +
";}";
b->clicked().connect(command);
}
void MyClass::textChanged(std::string incoming)
{
}
"command" variable is equal to the following when it is used for editor->doJavaScript(command)
"Wt3_3_0.$('oy4ycjy').editor = ace.edit(Wt3_3_0.$('oy4ycjy'));Wt3_3_0.$('oy4ycjy').editor.setTheme('ace/theme/monokai');Wt3_3_0.$('oy4ycjy').editor.getSession().setMode('ace/mode/javascript');"
"command" variable is equal to the following when it is used for b->clicked().connect(command);
"function(object, event) {Wt.emit('oy4ycjy','textChanged',Wt3_3_0.$('oy4ycjy').editor.getValue());;}"
UPDATE 1
Added the suggested code to my constructor, however the page does not change from a solid white screen. I am doing nothing else in this WT project, only this code is running.
wApp->require("lib/ace/ace.js");
// A WContainerWidget is rendered as a div
WContainerWidget *editor = new WContainerWidget(root());
std::string editor_ref = editor->jsRef(); // is a text string that will be the element when executed in JS
editor->doJavaScript(
editor_ref + ".editor = ace.edit('" + editor_ref + "');" +
editor_ref + ".editor.setTheme('ace/theme/monokai');" +
editor_ref + ".editor.getSession().setMode('ace/mode/javascript');"
);
The value of editor_ref is "Wt3_3_0.$('oumvrgm')" minus the quotes.
Also tried adding the code below, and the page is still blanked out.
JSignal <std::string> *jsignal = new JSignal<std::string>(editor, "textChanged");
jsignal->connect(this, &MyClass::textChanged);
WPushButton *b = new WPushButton("Save", root());
b->clicked().connect("function(object, event) {" +
jsignal->createCall(editor->jsRef() + ".editor.getValue()") +
";}");
I have also found that commenting out
editor_ref + ".editor = ace.edit('" + editor_ref + "');" +
makes the button show up, but there is a red "Loading..." note at the top right of the screen so WT is waiting on something.
I have textChanged as a do nothing function at the moment.
ORIGINAL POST
So, my problem is this. How can I get ACE http://ace.ajax.org/#nav=about in WT http://www.webtoolkit.eu/wt. More specifically, ACE in a WT Wt::WTextArea or Wt::WTabWidget, the text area would be preferred. I have been trying to do this for a few days now and have not had much success.
I've been able to embed ACE in an HTML page no problem, as their site says "just copy and paste it into your page" and it really is that simple. However, I need to load it locally through WT and into a container. I downloaded their repos from GIT to my machine and have tried using
require("lib/ace/ace.js");
and
doJavaScript(...);
with various commands to no success... I am not nearly as strong in Java and HTML as C++ so I will ask for as much detail as possible if this involves a lot of Java/HTML. Thanks in advance mates!
Maybe this puts you in the right direction:
wApp->require("lib/ace/ace.js")
// A WContainerWidget is rendered as a div
WContainerWidget *editor = new WContainerWidget(parent);
// editor->jsRef() is a text string that will be the element when executed in JS
editor->doJavaScript(
editor->jsRef() + ".editor = ace.edit(" + editor->jsRef() + ");" +
editor->jsRef() + ".editor.setTheme('ace/theme/monokai');" +
editor->jsRef() + ".editor.getSession().setMode('ace/mode/javascript');"
);
That should decorate the editor. Wt does not automatically send the modifications to a div to the server, so you do this manually through a JSignal (emits a signal from JS to C++):
JSignal <std::string> *jsignal = new JSignal<std::string>(editor, "textChanged");
jsignal->connect(this, MyClass::textChanged);
WPushButton *b = new WPushButton("Save", parent);
b->clicked().connect("function(object, event) {" +
jsignal->createCall(editor->jsRef() + ".editor.getValue()") +
";}");
(code above is not tested so you may need to adjust a bit)
I have integrated CodeMirror in an earlier JWt (java) project like this:
import eu.webtoolkit.jwt.WApplication;
import eu.webtoolkit.jwt.WContainerWidget;
import eu.webtoolkit.jwt.WTextArea;
public class CodeMirrorTextArea extends WContainerWidget {
private WTextArea textArea;
public CodeMirrorTextArea(WContainerWidget parent) {
super(parent);
textArea = new WTextArea(this);
WApplication app = WApplication.getInstance();
app.require(app.resolveRelativeUrl("codemirror-2.32/lib/codemirror.js"));
app.require(app.resolveRelativeUrl("codemirror-2.32/mode/groovy/groovy.js"));
//TODO:
//We save the editor state to the text area on each key stroke,
//it appears to be not a performance issue,
//however it might very well become one when editing larger fragments of code.
//A better solution would be to save this state to the text area only when
//the form is submitted, currently this is not yet possible in Wt???.
String js =
"var e = " + textArea.getJsRef() + ";" +
"var cm = CodeMirror.fromTextArea(e, {" +
" onKeyEvent : function (editor, event) {" +
" editor.save();" +
" }," +
" lineNumbers: true" +
" });" +
"var self = " + getJsRef() + ";" +
"self.cm = cm;";
this.doJavaScript(js);
}
public CodeMirrorTextArea() {
this(null);
}
public void setText(String text) {
textArea.setText(text);
}
public String getText() {
return textArea.getText();
}
public void setMarker(int line, String htmlMarker) {
String js =
"var self = " + getJsRef() + ";" +
"self.cm.setMarker(" + line + ", " + jsStringLiteral(htmlMarker +
"%N%") + ");";
this.doJavaScript(js);
}
public void clearMarker(int line) {
String js =
"var self = " + getJsRef() + ";" +
"self.cm.clearMarker(" + line + ");";
this.doJavaScript(js);
}
}
I have a game that uses ButtonSprite as some of the UI element sprites.
For each button I have a Texture Region for Normal and Pressed States.
mSomeBottonTextureRegion = BitmapTextureAtlasTextureRegionFactory.createFromAsset(this.mLoadTextureAtlas, this, "SomeBotton.png", 0, 0);
mSomeBottonPressedTextureRegion = BitmapTextureAtlasTextureRegionFactory.createFromAsset(this.mLoadTextureAtlas, this, "SomeBottonPressed.png", 0, 0);
ButtonSprite someButtonSprite = new ButtonSprite(0, 0, mSomeBottonTextureRegion, mSomeBottonPressedTextureRegion, mEngine().getVertexBufferObjectManager());
I see there are no constructors that handle a ButtonSprite with a single TiledTextureRegion for which you can supply a different tile for each state.
Am I missing something? Is there a way to do this with ButtonSprite? Or will I have to Extend a TiledSprite and add button functionality so that I only have to make one TextureRegion instead of two.
Thanks
You can just supply a TiledTextureRegion to the ButtonSprite as it is designed to handle 3 states - normal, pressed, and disabled. Check the ButtonSprite.java and you will see several alternate constructors that take a TiledTextureRegion and set the stateCount to the number of tiles in the TiledTextureRegion passed in.
Here's one of those constructors
public ButtonSprite(final float pX, final float pY, final ITiledTextureRegion pTiledTextureRegion, final VertexBufferObjectManager pVertexBufferObjectManager, final OnClickListener pOnClickListener) {
super(pX, pY, pTiledTextureRegion, pVertexBufferObjectManager);
this.mOnClickListener = pOnClickListener;
this.mStateCount = pTiledTextureRegion.getTileCount();
switch(this.mStateCount) {
case 1:
Debug.w("No " + ITextureRegion.class.getSimpleName() + " supplied for " + State.class.getSimpleName() + "." + State.PRESSED + ".");
case 2:
Debug.w("No " + ITextureRegion.class.getSimpleName() + " supplied for " + State.class.getSimpleName() + "." + State.DISABLED + ".");
break;
case 3:
break;
default:
throw new IllegalArgumentException("The supplied " + ITiledTextureRegion.class.getSimpleName() + " has an unexpected amount of states: '" + this.mStateCount + "'.");
}
this.changeState(State.NORMAL);
}
I have a series of Text- and Comboboxes along a jTable. I use the input from the boxes to filter the results of a JPQL-query, which are displayed on the jTable. Now, the idea was to automate the process, so that every time the user types a character in a box, the app sends the query automatically and updates the jTable with the filtered results. I tried doing it like this:
public class MyKeyListener extends KeyAdapter{
public void keyPressed(KeyEvent evt){
setFilters();
displayResults(); }}
setFilters() and displayResults() are the methods that respectively set the parameters and the query and get the ResultList and update the jTable. I then added the keyPressed to all relevant Textboxes, along with some System.out.println lines for debugging. What happens is following: when i enter the first character nothing happens. If i enter a second character the KeyListener works and sends the query, but filters only with the second character, the first one is ignored. That´s my first weirdness. The second one is, looking at my console i realized the query is beeing sent 6 times for every for every succesful Key Listened. Help would be greatly appreciated. On second thought, I´m also inserting my setFilters() and displayResults().
private void setFilters() {
//Auslesen der gesetzten Filter
String name=jName.getText()+"%";
String vorname= jVorname.getText()+"%";
String vvname= jVName.getText()+"%";
String vvorname=jVVorname.getText()+"%";
String mname=jMName.getText()+"%";
String strasse=jAdresse.getText()+"%";
String plz=jPlz.getText()+"%";
String ort=jOrt.getText()+"%";
String gruppe=jGruppe.getText()+"%";
String geschlecht=(String) jGeschlecht.getSelectedItem()+"%";
String mvorname=jMVorname.getText()+"%";
String firma=jFirma.getText()+"%";
//Die Query
kinderQuery = java.beans.Beans.isDesignTime() ? null : rcwPUEntityManager.createQuery("SELECT k FROM Kinder k "
+ "INNER JOIN k.vaeter vat "
+ "INNER JOIN k.muetter mut "
+ "INNER JOIN k.gruppen gru "
+ "INNER JOIN k.firmen fir "
+ "WHERE k.kindName LIKE :name "
+ "AND k.kindVorname LIKE :vorname "
+ "AND vat.vaterName LIKE :vname "
+ "AND vat.vaterVorname LIKE :vvorname "
+ "AND mut.mutterName LIKE :mname "
+ "AND mut.mutterVorname LIKE :mvorname "
+ "AND k.kindStrasse LIKE :strasse "
+ "AND k.kindPLZ LIKE :plz "
+ "AND k.kindOrt LIKE :ort "
+ "AND gru.gruppeName LIKE :gruppe "
+ "AND k.kindGeschlecht LIKE :geschlecht "
+ "AND fir.firmaName LIKE :firma ");
//Einsetzen der ausgelesenen Strings in die JPQL-Query Parameter
kinderQuery.setParameter("name", name);
kinderQuery.setParameter("vorname", vorname);
kinderQuery.setParameter("vname", vvname);
kinderQuery.setParameter("vvorname", vvorname);
kinderQuery.setParameter("mname", mname);
kinderQuery.setParameter("mvorname", mvorname);
kinderQuery.setParameter("strasse", strasse);
kinderQuery.setParameter("plz", plz);
kinderQuery.setParameter("ort", ort);
kinderQuery.setParameter("gruppe", gruppe);
kinderQuery.setParameter("geschlecht", geschlecht);
kinderQuery.setParameter("firma", firma);
}
private void displayResults(){
java.util.Collection data = kinderQuery.getResultList();
System.out.println(data);
kinderList.clear();
kinderList.addAll(data);
jTable1.repaint();
}
Thanks in advance !
The problem is that the Document used by the text field has not been updated when the keyPressed event is generated. You could listen for the keyTyped event, however I don't recommend this as it is not the best solution and Swing provides a better API to use.
The better way to do this is to use a DocumentListener which is specifically designed for this purpose. Read the section from the Swing tutorial on How to Write a Document Listener for an example and explanation.
Here's pretty much how to do it. A couple of benefits for you using this approach instead of the keyPressed is that it will work also when you paste some text into the fields and it's easy to avoid doing all your filtering on deletes etc.
// in your frame or panel
Document doc = new PlainDocument();
doc.addDocumentListener(new MyDocumentListener());
JTextField textField = new JTextField(15);
textField.setDocument(doc);
add(textField);
private class MyDocumentListener implements DocumentListener
{
public void insertUpdate(DocumentEvent documentEvent)
{
// Do your stuff here
System.out.println("insert detected!");
}
public void removeUpdate(DocumentEvent documentEvent)
{
// ignore this
}
public void changedUpdate(DocumentEvent documentEvent)
{
// ignore this
}
}
And to avoid doing search and filter round trips on every weird thing your users might input into the text fields you should consider doing your own implementation of PlainDocument that only accepts certain characters.
private class MyDocument extends PlainDocument
{
#Override
public void insertString(int offs, String str, AttributeSet a) throws BadLocationException
{
StringBuilder builder = new StringBuilder(getText(0, getLength()));
builder.insert(offs, str);
if (builder.toString().matches("[A-Za-z]*"))
{
super.insertString(offs, str, a);
}
}
}