I'm writing a little plugin to move the caret position of an Eclipse text editor to the other side of a selected block. Problem is that I don't find a nice way to discover whether the selection is left-to-right or right-to-left.
I understand there are these alternatives:
Use a CarretListener in some way. It seems unnecessary and I don't want to.
Get hold of the underlaying StyledText and compare selection with the caret position. Seems to break abstraction because I have to know how the editor is implemented. Another disadvantage is that you would have to use widgetOffset2ModelOffset methods on the text viewer to adjust positions.
Can't I get the caret position from my ITextEditor or ISelectionProvider or something?
Here is my code:
public class SwapCursorSelectionHandler extends AbstractHandler {
public Object execute( ExecutionEvent event )
{
ITextEditor editor;
try {
editor = (ITextEditor) HandlerUtil.getActivePartChecked( event );
} catch ( ExecutionException exc ) {
throw new RuntimeException( exc );
}
ITextSelection sel = (ITextSelection) editor.getSelectionProvider().getSelection();
// How to find out if sel is left-to-right or right-to-left?!
editor.selectAndReveal( ... );
return null;
}
}
Update: There doesn't seem to be a way to do this without using the StyledText. I think this is weird and I considers placing a bug report suggesting that selection direction information should be added to ITextSelection. Before I do this it would be interesting to get the opinion on people here at SO about this proposal.
EDIT: This solution turned out to be wrong! Thanks to willkil for pointing it out.
This not very elegant piece of code was the most canonical way to achieve this I could find. It uses ITextEditor.getAdapter(ITextOperationTarget.class) and JFaceTextUtil. This implies that it depends on a particular editor implementation, but at least I don't have to touch it myself or mess with the widget2model methods.
public Object execute(ExecutionEvent event) {
try {
ITextViewer viewer = (ITextViewer)
((ITextEditor) HandlerUtil.getActivePartChecked(event))
.getAdapter(ITextOperationTarget.class);
int caretOffset = JFaceTextUtil.getOffsetForCursorLocation(viewer);
} catch (ExecutionException exc) {
throw new RuntimeException(exc);
}
return null;
}
The best way to get the current cursor position is via ITextViewer.getTextWidget().getCaretOffset(). Here's an example that prints various text positions in an implementation of IContentAssistProcessor that I was working on:
public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) {
int widgetCaretOffset = viewer.getTextWidget().getCaretOffset();
if (viewer instanceof ITextViewerExtension5) {
ITextViewerExtension5 extension = (ITextViewerExtension5) viewer;
System.out.println(extension.widgetOffset2ModelOffset(widgetCaretOffset));
}
System.out.println(JFaceTextUtil.getOffsetForCursorLocation(viewer));
System.out.println(offset);
System.out.println(widgetCaretOffset);
System.out.println(viewer.getSelectedRange());
}
I placed the caret at a random place in a document, then moved the mouse close to the beginning of the first line, then triggered the content assistant with various selections. In my case, the textViewer does not implement ITextViewerExtension5, so only four lines print. The output of the code above is below:
With nothing selected:
6
794
794
Point {794, 0}
With a left-to-right selection created by shift-right (caret blinking on right side of selection):
6
794
799
Point {794, 5}
Note that the caret position is 799, which equals 794 + 5.
With a left-to-right selection created by shift-left (caret blinking on left side of selection):
6
794
794
Point {794, 5}
Note that the caret position equals the selection offset.
Also note, although it's not relevant to this question, that the offset parameter in IContentAssistProcessor. computeCompletionProposals() is always the offset of the selection, not the caret.
If you have an ITextEditor instead of an ITextViewer, you can get the ITextViewer via the method from another answer to this question and from an answer to a different question:
ITextEditor editor;
ITextOperationTarget target = (ITextOperationTarget) editor.getAdapter(ITextOperationTarget.class);
if (target instanceof ITextViewer) {
ITextViewer viewer = (ITextViewer) target;
int widgetCaretOffset = viewer.getTextWidget().getCaretOffset();
if (viewer instanceof ITextViewerExtension5) {
ITextViewerExtension5 extension = (ITextViewerExtension5) viewer;
System.out.println(extension.widgetOffset2ModelOffset(widgetCaretOffset));
}
System.out.println(JFaceTextUtil.getOffsetForCursorLocation(viewer));
System.out.println(offset);
System.out.println(widgetCaretOffset);
System.out.println(viewer.getSelectedRange());
}
Related
I'm making a spigot plugin (version 1.8.8) that has an function that I know works because it fires flawlessly through my command. However, when I call it at the end of a PlayerExpChangeEvent, it seems like vanilla leveling overrides the bar, making it go up way more that it is supposed to. Running the command/function after this happens makes the bar go back to how it is supposed to be. I've tried setting my event's priority to highest (and when that didn't work, to lowest) but no matter what my function appears to be completely ignored when called inside the event.
Here is some code:
#EventHandler(priority=EventPriority.HIGHEST)
public void onXpGain(PlayerExpChangeEvent event)
{
// Load custom levels from config
ArrayList<String> levelList = new ArrayList<String>(plugin.getConfig().getStringList("levels"));
if (!((String)levelList.get(0)).equals("none"))
{
Player player = event.getPlayer();
Iterator<String> var4 = levelList.iterator();
while (var4.hasNext())
{
String s = (String)var4.next();
String[] splits = s.split(" ");
int levelCompare = Integer.parseInt(splits[0]);
int playerLvl = player.getLevel();
// Detect if on correct tier, else continue iteration
if (playerLvl == levelCompare - 1)
{
// Calculate the player's new XP amount
int totalXp = player.getTotalExperience() + event.getAmount();
player.setTotalExperience(totalXp);
updateBar(event.getPlayer()); // <-- THIS IS THE FUNCTION
return;
}
}
// At max level
player.setTotalExperience(player.getTotalExperience() + event.getAmount());
player.setLevel(getHighestLevel(levelList));
player.setExp(1.0f);
}
}
And here is the function itself. Keep in mind that it works fine when called through a command and not an event. It's purpose is to use the player's total XP to set the level and bar. Neither set correctly in the event; it instead embraces vanilla leveling.
public static void updateBar(Player player) {
ArrayList<String> levelList = new ArrayList<String>(plugin.getConfig().getStringList("levels"));
int totalXp = player.getTotalExperience();
player.setLevel(getHighestLevelForXp(totalXp, levelList));
if (player.getLevel() < getHighestLevel(levelList)) {
int lvlDiff = getTotalXpToLevel(player.getLevel() + 1,levelList) - getTotalXpToLevel(player.getLevel(),levelList);
int xpDiff = totalXp - getTotalXpToLevel(player.getLevel(),levelList);
player.setExp((float)xpDiff/lvlDiff);
} else {
player.setLevel(getHighestLevel(levelList));
player.setExp(0.0f);
}
return;
}
The command where the function works correctly is a bare-bones call to the function and doesn't need a mention here. Does anyone know how to get my event to override vanilla xp gain? The update works through the command, just not natural xp gain. It is already confirmed that the event DOES fire, as the rest of the event changes the internal xp amount, but the visual effects are overridden by vanilla. Can anyone help? Thanks.
Only setting the Player's EXP won't be enough for your desired behaviour. The Vanilla behaviour will still complete, as you're not changing how the event will add EXP to the player.
Currently, your event is working like this:
And PlayerExpGainEvent isn't cancellable, so you cannot undo it's addition of EXP.
What you can do instead is to set the EXP the event will add to 0, therefore not changing the player's EXP after your interception.
event.setAmount(0); //Cancelling the EXP addition
I would recommend to set your event to a high priority, so that other events that depend on Experience gain won't trigger when you set the amount gained to 0.
I just got into Editor Scripting recently and I'm still getting a hold of all the basics. I wanted to know if the IntSlider (that we use to create a slider of integer values) has any methods or functions that can be used to get more functionality?
Something like the Slider class:
You have -maxValue, -minValue, -value, -onValueChanged (Callback executed when the value of the slider is changed)
These work during the "Play" mode.
I need to access this information while in the editor. Specifically: I need to access the onValueChanged (if it exists) of the IntSlider so I can assign a function for it to execute.
IntSlider Code:
totalRooms = EditorGUILayout.IntSlider(new GUIContent ("Total Rooms"), totalRooms, 0, 10);
Is there a way to achieve this for the inspector? Or something that can be created to solve this? If you can point me in the right direction, I'd be grateful.
I'm using Unity 5.3.1f
There is no such event in Unity at the moment. However this can be implemented easily like this:
Add a UnityEvent in your actual script.
public UnityEvent OnVariablesValueChanged;
Then in your editor script check if value is changed. (Note: I tried to write easily understandable code sample for this. you can change it accordingly)
[CustomEditor(typeof(MyScript))]
public class MyScriptEditor : Editor
{
public override void OnInspectorGUI()
{
MyScript myTarget = (MyScript)target;
int prevValue = myTarget.variable;
myTarget.variable = EditorGUI.IntSlider(new Rect(0,0,100, 20), prevValue, 1, 10);
int newValue = myTarget.variable;
if (prevValue != newValue)
{
Debug.Log("New value of variable is :" + myTarget.variable);
myTarget.OnVariablesValueChanged.Invoke();
}
base.OnInspectorGUI();
}
}
Then add listener to OnVariablesValueChanged event from inspector.
Hope this helps.
I'm working in Eclipse and I want to know if I can make an if statement that checks to see if the BufferedImage has been painted/drawn onto the frame. For some reason, it's not painting the correct image because clickable regions are appearing on that picture when they are not supposed to.
For example, when I click the region to go from 4>5 everything is good. When I click from 5 to go to 4 I end up at 6 because the 'regions' from 4 are appearing in 5 (The image should always be painted before the clickable regions are shown) before it's even being painted. I want to restrict this to check if the image has been painted onto the frame first.
I really don't want to use anything else besides what I have right now (so no new classes being implemented to do this task), I really just want a simple yet effective way to resolve this. Here is what I'm talking about:
...
MouseAdapter mouseHandler = new MouseAdapter()
{
public void mouseClicked(MouseEvent e)
{
repaint();
if(n==0)
{
if(e.getX()>=459 && e.getX()<491 && e.getY()>=111 && e.getY()<133
{
n = 4;
}
return;
}
if(n==5)
{
if(...)
{
n = 4;
}
return();
}
if(n==6)
{
if(...)
{
n = 5;
}
if(...)
{
n = 0;
}
if(...)
{
n = 6;
}
return();
}
}
...
I think you might need to give a little more information. The problem might lie in how you repaint, not whether it was painted.
If you are running another thread as your main program, you might instead want to send the mouse events synchronously to that so that the main thread can process the mouse click and then repaint.
Another solution might be to override the repaint method and paint the buffered images there, but you may have done that.
Also, a little off topic, I noticed that you used for loops to determine if the mouse was clicked in a specific area.
You could shorten the code:
for(int i=459; i<491; i++){
if(e.getX()==i){
for(int j=111; j<133; j++){
if(e.getY()==j){
//action taken
}
}
}
}
To:
if(e.getX()>=459 && e.getX()<491 && e.getY()>=111 && e.getY()<133{
//action taken
}
This would take up less space in your code and less time checking every pixel.
Back to your question.
I dont know of a function to tell if a buffered image has been painted. The ploblem that you are having though might of might not be in the code provided. Posting the rest of your code would be beneficial.
Okay I found the solution, I forgot to come back to this question and let you know. The problem was that the mouse was double clicking for some reason. You could almost say it was 'recursive'. I decided to move the mouseListener from inside the paintComponent to outside of it, and surely enough that fixed it.
As the title shows, I want to add a listener to my rcp user interface in order to detect maximization and minimization. Actually, it not that my real purpose, but I think it is a way to solve my problem. I have a view with some shapes in the center, and I wonna keep the drawing exactly in the center even if the window is resized. To do so, I used the following listener :
public void createPartControl(final Composite parent) {
display = parent.getDisplay();
white= display.getSystemColor(SWT.COLOR_WHITE);
parent.setLayout(new FillLayout(SWT.VERTICAL));
final ScrolledComposite sc = new ScrolledComposite(parent, SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
sc.setExpandHorizontal(true);
sc.setExpandVertical(true);
sc.setMinHeight(100);
sc.setMinWidth(100);
sc.setSize(565, 305);
final Composite child = new Composite(sc,SWT.NONE);
child.setLayout(new FillLayout());
// Set child as the scrolled content of the ScrolledComposite
sc.setContent(child);
child.setBackground(white);
gc = new GC(child);
parent.addListener (SWT.Resize, new Listener () {
public void handleEvent (Event e) {
x = child.getBounds().width/2;
y = child.getBounds().height/2;
child.addPaintListener(new PaintListener() {
public void paintControl(PaintEvent event) {
dessin(gc); // draw my shapes
}
});
}
everything goes well except when I maximize the window and then minimize it, in this case I loose the drawing (it is in the corner).
Any idea please? I'm I thinking in the right way?
The two events to detect minimization and un-minimization (not necessarily maximization) are Iconify and Deiconify which only occur on the Shell. See the javadocs for Shell.
Consider moving the resize event is seen for the parent, as the child need not necessarily be resized yet.
In order to keep something in the center of something else all you need is the SWT.Resize event, so this question is a classic case of the XY Problem. (Except that the OP in this case seems to already suspect that this may be an XY Problem.)
However, many people arrive at this question with a legitimate need to programmatically detect window minimized / maximized / restored events, for the following reason:
If you want to be able to save the bounds of your application window on exit, you cannot just save whatever is returned by Shell.getBounds(), because your application may be terminated while minimized or maximized or fullscreen, in which case its bounds should not be persisted. What should be persisted is the minimized/normal/maximized/fullscreen state of the shell, (I call it "posture",) and the bounds of the shell last time its posture was "normal". So, essentially, you need to keep track of when the posture is "normal", and for that you need to have a "posture changed" event.
The problem is that when SWT issues the "deiconified" event, it has not calculated the bounds of the shell yet, so the value that you get in that case is bogus.
So, here is the solution to that:
You are going to need a method which recalculates the posture as follows:
private void recalculatePosture()
{
Posture posture = swtShell.getFullScreen()? Posture.FULLSCREEN
: swtShell.getMinimized()? Posture.MINIMIZED
: swtShell.getMaximized()? Posture.MAXIMIZED
: Posture.NORMAL;
if( posture != previousPosture )
{
issue event...
previousPosture = posture;
}
}
In order to generate the "maximized", "restored (from maximized)" and "fullscreen" events you can use Shell.addListener() to listen for the SWT.Move and SWT.Resize event, and invoke recalculatePosture() when they occur.
In order to generate the "minimized" event you can use the shellIconified() method of the ShellListener as #the.duckman said, and again, invoke recalculatePosture().
In order to generate the "restored (from minimized)" event, you need to do the following in your ShellListener:
#Override
protected void onShellDeiconified( ShellEvent e )
{
display.asyncExec( () -> recalculatePosture() );
}
This will cause the recalculation of posture a short time after the 'deiconified' event, at which point SWT will have gotten around to properly calculating the bounds of the shell.
I have a mouse listener. It has some code to respond to mouseUp and mouseDown events. This works correctly.
However, as soon as I add a DragSource, my mouseDown event is no longer delivered -- until I release the mouse button!
This is trivial to reproduce - below is a simple program which contains a plain shell with just a mouse listener and a drag listener. When I run this (on a Mac), and I press and hold the mouse button, nothing happens - but as soon as I release the mouse button, I instantly see both the mouse down and mouse up events delivered. If I comment out the drag source, then the mouse events are delivered the way they should be.
I've searched for others with similar problems, and the closest I've found to an explanation is this:
https://bugs.eclipse.org/bugs/show_bug.cgi?id=26605#c16
"If you hook drag detect, the operating system needs to eat mouse events until it determines that you have either dragged or not."
However, I don't understand why that's true -- why must the operating system eat mouse events to determine if I have a drag or not? The drag doesn't start until I have a mouse -move- event with the button pressed.
More importantly: Can anyone suggest a workaround? (I tried dynamically adding and removing my drag source when the mouse is pressed, but then I couldn't get drag & drop to function properly since it never saw the initial key press - and I can't find a way to programmatically initiate a drag.)
Here's the sample program:
package swttest;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSource;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.DragSourceListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
public class SwtTest {
public static void main(String[] args) {
final Display display = new Display();
final Shell shell = new Shell(display);
shell.addMouseListener(new MouseListener() {
public void mouseUp(MouseEvent e) {
System.out.println("mouseUp");
}
public void mouseDown(MouseEvent e) {
System.out.println("mouseDown");
}
public void mouseDoubleClick(MouseEvent e) {
System.out.println("mouseDoubleClick");
}
});
DragSourceListener dragListener = new DragSourceListener() {
public void dragFinished(DragSourceEvent event) {
System.out.println("dragFinished");
}
public void dragSetData(DragSourceEvent event) {
System.out.println("dragSetData");
}
public void dragStart(DragSourceEvent event) {
System.out.println("dragStart");
}
};
DragSource dragSource = new DragSource(shell, DND.DROP_COPY | DND.DROP_MOVE);
dragSource.addDragListener(dragListener);
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}
}
To answer your specific question about why this happens -- on Cocoa we don't consider a drag to have started until the mouse has moved a few pixels. This ensures against 'accidental' drags if you're sloppy with the clicks. On Linux and Win32 the window toolkit can do the drag detection. If you just hold down the button the detection times out and the mouse down is delivered. On Cocoa we have no time out, which is why nothing happens until the drag is detected or a mouse up happens.
That's a lot of detail, but the conclusion is that the behavior is inconsistent, and we should always be able to deliver the mouse down immediately, without waiting for the drag detection to complete.
I don't see a workaround, since this is happening before the Control sees the event.
See this bug which has patches for win32, gtk and cocoa SWT.
I had faced the same problem and found a solution. Once you attach a DragSource to your custom widget, the event loop will be blocked in that widget's mouse down hook and will eat mouse move events to detect a drag. (I've only looked into the GTK code of SWT to find this out, so it may work a little differently on other platforms, but my solution works on GTK, Win32 and Cocoa.) In my situation, I wasn't so much interested in detecting the mouse down event right when it happened, but I was interested in significantly reducing the drag detection delay, since the whole purpose of my Canvas implementation was for the user to drag stuff. To turn off the event loop blocking and built-in drag detection, all you have to do is:
setDragDetect(false);
In my code, I am doing this before attaching the DragSource. As you already pointed out, this will leave you with the problem that you can't initiate a drag anymore. But I have found a solution for that as well. Luckily, the drag event generation is pure Java and not platform specific in SWT (only the drag detection is). So you can just generate your own DragDetect event at a time when it is convenient for you. I have attached a MouseMoveListener to my Canvas, and it stores the last mouse position, the accumulated drag distance and whether or not it already generated a DragDetect event (among other useful things). This is the mouseMove() implementation:
public void mouseMove(MouseEvent e) {
if (/* some condition that tell you are expecting a drag*/) {
int deltaX = fLastMouseX - e.x;
int deltaY = fLastMouseY - e.y;
fDragDistance += deltaX * deltaX + deltaY * deltaY;
if (!fDragEventGenerated && fDragDistance > 3) {
fDragEventGenerated = true;
// Create drag event and notify listeners.
Event event = new Event();
event.type = SWT.DragDetect;
event.display = getDisplay();
event.widget = /* your Canvas class */.this;
event.button = e.button;
event.stateMask = e.stateMask;
event.time = e.time;
event.x = e.x;
event.y = e.y;
if ((getStyle() & SWT.MIRRORED) != 0)
event.x = getBounds().width - event.x;
notifyListeners(SWT.DragDetect, event);
}
}
fLastMouseX = e.x;
fLastMouseY = e.y;
}
And that will replace the built-in, blocking drag detection for you.