Related
So, my issue is this: I'm attempting to define a custom set of nodes for a Javafx XYChart LineChart, where each node corresponds to a point that was plotted directly from the datasets. After looking around a little bit, Jewlesea actually had a solution at one point about how to add dynamic labels to nodes on a linechart graph that gave me enough of a push in the right direction to create black symbols (they are dots at the moment, but they can be many different things). Now I have a requirement that requires me to change ONE of the nodes on the XY chart into an 'X'. this could be either through loading an image in place of the 'node', or through physically manipulating the 'shape' parameter in .css.
The problem begins when I try to add this property dynamically, since which node has the 'x' will always be changing. Here are the things I've tried, and they all end up with no results whatsoever, regardless of the property used.
private XYChart.Data datum( Double x, Double y )
{
final XYChart.Data data = new XYChart.Data(x, y);
data.setNode(
new HoveredThresholdNode(x, y));
//data.getNode().setStyle("-fx-background-image: url(\"redX.png\");");
data.getNode().styleProperty().bind(
new SimpleStringProperty("-fx-background-color: #0181e2;")
.concat("-fx-font-size: 20px;")
.concat("-fx-background-radius: 0;")
.concat("-fx-background-insets: 0;")
.concat("-fx-shape: \"M2,0 L5,4 L8,0 L10,0 L10,2 L6,5 L10,8 L10,10 L8,10 L5,6 L2,10 L0,10 L0,8 L4,5 L0,2 L0,0 Z\";")
);
data.getNode().toFront();
return data;
}
So in the above, you can see that this is adding a property through the use of the 'bind' function after the dataNode has already been created. Also note above, I tried doing it through the 'setStyle' interface at this level to give it a background image, with no success. Also, no errors are being thrown, no 'invalid css' or anything of the sort, just simply no display on the graph at all when done this way.
now, in the HoveredThresholdNode (Again a big thanks to Jewelsea for being a master of Javafx and putting this bit of code online, it's where 90% of this class came from.) I tried essentially the same thing, at a different level. (actually being IN the node creation class, as opposed to a layer above it).
class HoveredThresholdNode extends StackPane {
/**
*
* #param x the x value of our node (this gets passed around a bunch)
* #param y the y value of our node (also gets passed around a bunch)
*/
HoveredThresholdNode(Double x, Double y) {
//The preferred size of each node of the graph
//getStylesheets().add(getClass().getResource("style/XYChart.css").toExternalForm());
//getStyleClass().add("xyChart-Node");
//setOpacity(.8);
styleProperty().bind(
new SimpleStringProperty("-fx-background-color: #0181e2;")
.concat("-fx-font-size: 20px;")
.concat("-fx-background-radius: 0;")
.concat("-fx-background-insets: 0;")
.concat("-fx-shape: \"M2,0 L5,4 L8,0 L10,0 L10,2 L6,5 L10,8 L10,10 L8,10 L5,6 L2,10 L0,10 L0,8 L4,5 L0,2 L0,0 Z\";")
);
//this label is the 'tooltip' label for the graph.
final Label label = createDataThresholdLabel(x, y);
final double Myx = x;
final double Myy = y;
setOnMouseEntered(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
if (Myx == 0) {
label.setTextFill(Color.DARKGRAY);
} else if (Myx > 0) {
label.setTextFill(Color.SPRINGGREEN);
} else {
label.setTextFill(Color.FIREBRICK);
}
label.setText("Current position: " + Myx + " , " + Myy);
//setCursor(Cursor.NONE);
toFront();
}
});
setOnMouseExited(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
//getChildren().clear();
//setCursor(Cursor.CROSSHAIR);
}
});
}
Now note, I also tried the setStyle(java.lang.String) method, with all of the same type of CSS, with no success. I have NO idea why this isn't styling dynamically. It's almost as if the custom nodes are simply ignoring all new .css that I define at runtime?
Any help would be greatly appreciated, please don't be shy if you need more details or explanation on any points.
So, I did finally find a good workaround to solve my problem, although not in the way I thought it would happen. The main problem I was having, was that I was extending from stackPane to create my node, which only had a very small number of graphical display options available to it, and by switching the 'prefSize()' property, I was simply changing the size of that stackPane, and then filling in the background area of that stack pane black, giving it a very deceptive shape-look to it.
So rather than use a stack pane, whenever I reached the node that I needed to place the red 'X' on, I simply called a different Datum method that returned a datum with an ImageView Attached, like so:
private XYChart.Data CoLDatum(Double x, Double y){
final XYChart.Data data = new XYChart.Data(x, y);
ImageView myImage = new ImageView(new Image(getClass().getResource("style/redX.png").toExternalForm()));
data.setNode(myImage);
data.getNode().setOnMouseEntered(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
main_label.setText("Some Text.");
}
});
data.getNode().setOnMouseExited(new EventHandler<MouseEvent>(){
#Override public void handle(MouseEvent mouseEvent) {
main_label.setText("");
}
});
return data;
}
and since ImageView is an implementing class of Node, this worked out just fine, and allowed me to load up an image for that one single node in the graph, while still maintaining a listener to give custom text to our information label when the red 'x' was hovered over with a mouse. Sometimes, it's the simple solutions that slip right past you.
I imagine that, had I employed stackPane properties properly with the setStyle(java.lang.String) method, they would have absolutely shown up, and I was just butchering the nature of a stack pane. Interesting.
Hopefully this helps somebody else stuck with similar problems!
I have a ScrollView with a lot buttons. Each button is enabled when the user unlocks that button/level. I would like to focus the ScrollView on the latest unlocked button/level. See the screenshot below.
I found some functions like scrollTo() that could focus the ScrollView either at top, button or something like that but I would like to focus the ScrollView at certain places like the button ID that says Level 8 in the screenshot below. How would I go about doing this?
I think this should be good enough :
yourButtonView.getParent().requestChildFocus(yourButtonView,yourButtonView);
public void RequestChildFocus (View child, View focused)
child - The child of this ViewParent that wants focus. This view will contain the focused view. It is not necessarily the view that actually has focus.
focused - The view that is a descendant of child that actually has focus
First you need to know the position of item in scroll that has to get focus once you know that you can use following code to make that item focus
final int x;
final int y;
x = rowview[pos].getLeft();
y = rowView[pos].getTop();
yourScrollView.scrollTo(x, y);
refer this question
I agree with the benefits of previous answers. However, by experience, I have found this to be more complex than this. The setSelectionFromTop is very sensitive and often breaks if it is executed too early. This may depend on different reasons but the two primary reasons are that
If executed from within Activity lifecycle methods the views have not been loaded/configured yet.
View modifications triggered after the list move action seems to break the move. Probably overwriting some value before the move has been finalized due to a recalculation of the views.
The reasoning seems to apply for both setSelectionFromTop and setSelection() methods although
I tested mostly with setSelectionFromTop. smoothScrollToPosition() seems to be more robust, probably because it by definition changes the list position delayed whn doing the smooth scrolling.
Look at this example source code:
// Wee need to pospone the list move until all other view setup activities are finished
list.post(new Runnable(){
#override
public void run() {
list.setSelectionFromTop(selectedPosition, Math.max(0, Math.round(list.getHeight() / 3))); // Make sure selection is in middle of page, if possible.
// Make sure you do not modify (this or other) parts of the view afterwards - it may break the list move
// activityButtons.setVisibility(View.GONE);
}});
see: http://developer.android.com/reference/android/widget/ListView.html#setSelectionFromTop(int, int)
'int selectedLevel' holds the currently selected level, information is held in a Level class.
'values' is a list with all your levels, displayed by the list.
Example:
ListView lv = (ListView)findViewById(android.R.id.list);
int i = 0;
for (Level l : values) {
i++;
if (l.level == selectedLevel) {
lv.setSelectionFromTop(i, 200);
break;
}
}
try this
sc.post(new Runnable() {
public void run() {
sc.smoothScrollTo(x, y);
}
});
x the position where to scroll on the X axis
y the position where to scroll on the Y axis
Use this code, taken from this answer
private final void focusOnView(){
new Handler().post(new Runnable() {
#Override
public void run() {
your_scrollview.scrollTo(0, your_EditBox.getBottom());
}
});
}
Use this code, taken from my another answer
scrollViewSignup.post(new Runnable() {
#Override
public void run() {
int scrollY = scrollViewSignup.getScrollY();
scrollViewSignup.scrollTo(0, 0);
final Rect rect = new Rect(0, 0, view.getWidth(), view.getHeight());
view.requestRectangleOnScreen(rect, true);
int new_scrollY = scrollViewSignup.getScrollY();
scrollViewSignup.scrollTo(0, scrollY);
scrollViewSignup.smoothScrollTo(0, new_scrollY);
}
});
This code tries to smooth scroll and uses the standard behaviour of system to position to an item. You can simply change smoothScrollTo with scrollTo if you don't want the system to smooth scroll to the item. Or, you can use the code below only.
scrollViewSignup.post(new Runnable() {
#Override
public void run() {
scrollViewSignup.scrollTo(0, 0);
final Rect rect = new Rect(0, 0, view.getWidth(), view.getHeight());
view.requestRectangleOnScreen(rect, true);
}
});
Use the desired code block after trying, or, according to the need.
I'm trying to find a way to change the line spacing in a JTextArea component.
A little bit of searching always seems to reveal the same answer: "Use a JTextPane instead and then call setParagraphAttributes".
But I'm wondering whether it's possible to achieve this with just a JTextArea by, for example, messing with the font.
With the deriveFont(...) method, it's possible to change the tracking and the kerning of the font, i.e. the horizontal spacing between characters, but I haven't been able to find a way to change the vertical spacing (maybe ascent, descent, leading). Am I missing something there?
As camickr pointed out, JTextArea does not provide a way to change the line height directly. It simply uses the font height provided by the corresponding FontMetrics.
But, this leaves a point of attack open using the following helper-class:
public class FontMetricsWrapper extends FontMetrics {
private final FontMetrics target;
public FontMetricsWrapper(FontMetrics target) {
super(target.getFont());
this.target = target;
}
#Override
public int bytesWidth(byte[] data, int off, int len) {
return target.bytesWidth(data, off, len);
}
#Override
public int charWidth(char ch) {
return target.charWidth(ch);
}
#Override
public int charWidth(int codePoint) {
return target.charWidth(codePoint);
}
// ... tons more #Override's, all of the same form:
//
// #Override
// public baz foo(bar, ...) {
// return target.foo(bar, ...);
// }
}
Then, it becomes possible to create the JTextArea like this:
JTextArea myTextArea = new JTextArea("Some text") {
#Override
public FontMetrics getFontMetrics(Font font) {
return new FontMetricsWrapper(super.getFontMetrics(font)) {
#Override
public int getHeight() {
return 10; // Gives line height in pixels
}
};
}
};
This is definitely not the cleanest solution and is merely meant as proof of concept. For example, one issue is that getFontMetrics(...) is called quite often, and, in the given example, creates a new instance of the wrapper class each time. So, at the very least, a HashMap that caches the created FontMetricsWrapper for each given font would be in order...
But, what I was really hoping for was a way to play with the Font or maybe the associated FontRenderContext passed into the JTextArea to modify the line height. For example, is there some way to influence font's reported ascent, descent, and leading values? Seems strange that you can change pretty much any other aspect of the font's appearance, except this one...
What's wrong with light-weight coding?
Nothing, you should use the simplest component for the job. But if the simple component doesn't support a requirement it is usually because the requirement is more complex and you need a more complex component to implement the functionality. Rarely would it be as simple at setting a property of a class.
Sometimes I'm simply interested in learning something new and figuring out what's possible.
Swing text components use a View to paint the text. It is the view's responsibility to format and position the text. So each view determines when to wrap and where to position the next line.
In the case of a JTextArea it uses either a Plainview or a WrappedPlanView. For the Plainview the painting code is:
drawLine(line, g, x, y);
y += fontHeight;
where the fontHeight is determined by using the FontMetrics.getHeight() method.
So the value is basically hard coded in the View. You could always provide a custom View for your text area, but overriding a View is generally not an easy task.
I am trying to check to see whether the android navigation bar is present on load so that I can adjust a layout accordingly, does anyone have any suggestions?
This is the navigation bar I am trying to detect:
P.S. All I have found so far are 'bad' ways to try and remove the bar, which I dont want to do.
Took me some time but I've found a more reliable way than relying on hasPermanentMenuKey() which doesn't work for newer phones like the HTC One which have no menu key but do have home & back keys so don't need (or show) the soft navigation bar. To get around this try the following code which checks for a back button too:
boolean hasMenuKey = ViewConfiguration.get(context).hasPermanentMenuKey();
boolean hasBackKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);
if(!hasMenuKey && !hasBackKey) {
// Do whatever you need to do, this device has a navigation bar
}
There's no reliable way to check for a navigation bar. Using KeyCharacterMap.deviceHasKey you can check if certain physical keys are present on the device, but this information is not very useful since devices with physical keys can still have a navigation bar. Devices like the OnePlus One, or any device running a custom rom, have an option in the settings that disables the physical keys, and adds a navigation bar. There's no way to check if this option is enabled, and deviceHasKey still returns true for the keys that are disabled by this option.
This is the closest you can get:
boolean hasBackKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);
boolean hasHomeKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_HOME);
if (hasBackKey && hasHomeKey) {
// no navigation bar, unless it is enabled in the settings
} else {
// 99% sure there's a navigation bar
}
If the back and home button are not both physically present on the device, it must have a navigation bar, because the user otherwise wouldn't be able to navigate at all. However, you can never be 100% sure about this, since manufacturers can implement deviceHasKey wrong.
Another solution
(a part of my class UtilsUISystem )
public static boolean hasNavBar (Resources resources)
{
//Emulator
if (Build.FINGERPRINT.startsWith("generic"))
return true;
int id = resources.getIdentifier("config_showNavigationBar", "bool", "android");
return id > 0 && resources.getBoolean(id);
}
Here is a quick answer that combines Pauland's and Philask's solutions. I'm afraid I don't have enough devices available to test if it works everywhere, though. I'd be interested to hear others' results.
boolean hasNavBar(Context context) {
Resources resources = context.getResources();
int id = resources.getIdentifier("config_showNavigationBar", "bool", "android");
if (id > 0) {
return resources.getBoolean(id);
} else { // Check for keys
boolean hasMenuKey = ViewConfiguration.get(context).hasPermanentMenuKey();
boolean hasBackKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);
return !hasMenuKey && !hasBackKey;
}
}
I've done like this, it works on every device I tested, and even on emulators:
public static boolean hasNavigationBar(Activity activity) {
Rect rectangle = new Rect();
DisplayMetrics displayMetrics = new DisplayMetrics();
activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(rectangle);
activity.getWindowManager().getDefaultDisplay().getRealMetrics(displayMetrics);
return displayMetrics.heightPixels != (rectangle.top + rectangle.height());
}
you could add this code to your activity's onCreate() method:
View decorView = getWindow().getDecorView();
decorView.setOnSystemUiVisibilityChangeListener
(new View.OnSystemUiVisibilityChangeListener() {
#Override
public void onSystemUiVisibilityChange(int visibility) {
if ((visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0) {
// TODO: The navigation bar is visible. Make any desired
// adjustments to your UI, such as showing the action bar or
// other navigational controls.
} else {
// TODO: The navigation bar is NOT visible. Make any desired
// adjustments to your UI, such as hiding the action bar or
// other navigational controls.
}
}
});
This method worked for me
int id = getResources().getIdentifier("config_showNavigationBar","bool","android");
boolean result = id > 0 && getResources().getBoolean(id);
//
if(result) {
// Do whatever you need to do, this device has a soft Navigation Bar
}
It worked for me and tested in many devices.
Something that should probably work better is to measure the screen.
Starting with API 17 there's getWindowManager().getDefaultDisplay().getRealSize(), which can be compared to size returned by getWindowManager().getDefaultDisplay().getSize().
If you get different results I think it's safe to say that there is a nav bar and if you get the same results there isn't one. One thing to pay attention to is your target SDK and supported screens, which might cause the result of getSize() to be scaled if Android thinks your app wouldn't work well on the current device without scaling.
Below API 17 you can measure the screen via getWindowManager().getDefaultDisplay().getMetrics() in both landscape and portrait mode, and again, different results probably mean there's a nav bar.
However, if you get the same results, you don't actually know, as phones can keep the nav bar on the shorter edge even when in landscape. An educated guess would be that if either the width or the height is 4% to 8% smaller than standard sizes like 1280x800, 1280x720, 1024x600, while the other dimension is equal, then again there probably is a nav bar. Don't bet on it, though. There are too many resolutions, which differ too little from one another for this to work well.
boolean hasNavBar(Context context) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
// navigation bar was introduced in Android 4.0 (API level 14)
Resources resources = context.getResources();
int id = resources.getIdentifier("config_showNavigationBar", "bool", "android");
if (id > 0) {
return resources.getBoolean(id);
} else { // Check for keys
boolean hasMenuKey = ViewConfiguration.get(context).hasPermanentMenuKey();
boolean hasBackKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);
return !hasMenuKey && !hasBackKey;
}
} else {
return false;
}
}
On Android 10 (API level 29), you can also check for the bottom window inset:
#RequiresApi(api = Build.VERSION_CODES.Q)
private boolean hasNavigationBar() {
final WindowInsets windowInsets = getWindow().getDecorView().getRootWindowInsets();
if (windowInsets == null) {
throw new RuntimeException("Window is not attached");
}
return windowInsets.getTappableElementInsets().bottom > 0;
}
Note that the window has to be attached for getRootWindowInsets() to return a non-null value, so you likely want to call this in onAttachedToWindow.
This solution is also used by LineageOS's launcher app Trebuchet (source), which is how I learned of it.
I see the answers above, I want to indicate
that the "not exist" can be regard as the height of 0;
so it can be like this:
public static int getScreenH(Context context) {
DisplayMetrics dm = new DisplayMetrics();
dm = context.getResources().getDisplayMetrics();
int h = dm.heightPixels;
return h;
}
public static int getDpi(Context context) {
DisplayMetrics displayMetrics1 = context.getResources().getDisplayMetrics();
int height1 = displayMetrics1.heightPixels;
int dpi = 0;
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = windowManager.getDefaultDisplay();
DisplayMetrics displayMetrics = new DisplayMetrics();
#SuppressWarnings("rawtypes")
Class c;
try {
c = Class.forName("android.view.Display");
#SuppressWarnings("unchecked")
Method method = c.getMethod("getRealMetrics", DisplayMetrics.class);
method.invoke(display, displayMetrics);
dpi = displayMetrics.heightPixels;
} catch (Exception e) {
e.printStackTrace();
}
return dpi;
}
public static int getBottomStatusHeight(Context context) {
int totalHeight = getDpi(context);
int contentHeight = getScreenH(context);
return totalHeight - contentHeight;
}
```
Solution: Only devices without permanent hardware keys have the navigation bar hence you can check for the API version and use hasPermanentMenuKey() to find hardware keys
boolean hasMenuKey = ViewConfiguration.get(context).hasPermanentMenuKey();
So, I have this JTexrtArea which is almost perfect for my needs. The only thing wrong with it is the line spacing. I can't set it. (Why not JTextPane? Because spacing CAN be changed in JTextArea and JTextArea is way lighter thatn JTextPane, and I have a bunch of those in my program).
I have asked this question before, and this is the answer that I got from user StanislavL:
To override JTextArea's line spacing take a look at the PlainView (used to render PLainDocument).
There are following lines in the public void paint(Graphics g, Shape a) method
drawLine(line, g, x, y);
y += fontHeight;
So you can adapt the rendering fixing y offset.
In the BasicTextAreaUI method to create view. Replace it with your own implementation of the PlainView
public View create(Element elem) {
Document doc = elem.getDocument();
Object i18nFlag = doc.getProperty("i18n"/*AbstractDocument.I18NProperty*/);
if ((i18nFlag != null) && i18nFlag.equals(Boolean.TRUE)) {
// build a view that support bidi
return createI18N(elem);
} else {
JTextComponent c = getComponent();
if (c instanceof JTextArea) {
JTextArea area = (JTextArea) c;
View v;
if (area.getLineWrap()) {
v = new WrappedPlainView(elem, area.getWrapStyleWord());
} else {
v = new PlainView(elem);
}
return v;
}
}
return null;
}
I grasp the general idea of what he's telling me to do, but I don't know how to do it.
Also, I wouldn't like to override the default JTextArea "property", I'd like to have a choice - to use the default one or to use a custom one.
Only change in JTextArea code would be from
y += fontHeight,
to
y+= (fontHeight +(or -) additionalSpacing).
How do I achieve this?
Which classes do I use/copy?
Where do I put them?
How do I make them usable?
How do I get the whole thing working?
If you think this is too specific to be useful, maybe someone could write a general tutorial on how to create a custom swing component based 100% on an existing one. Then someone could easely change some values to better adjust it to it's needs.
I am simply going to copy-paste my answer from your other question.
I'd like to change the spacing inbetweem the rows of a JTextArea
My first thought was that overriding javax.swing.JTextArea#getRowHeight would be sufficient. The javadoc clearly states
Defines the meaning of the height of a row. This defaults to the height of the font.
So I was hoping that by overriding this method, you would adjust the definition and you would get more spacing between the rows. Bummer, didn't work. A quick search on the usages of that method in the JDK revealed the same. It is mainly used to calculate some sizes, but certainly not used when painting text inside the component.
By looking at the source code of the javax.swing.text.PlainView#paint method, I saw that the FontMetrics are used, and those you can easily override in the JTextArea. So second approach was to extend the JTextArea (bwah, extending Swing components but it is for a proof-of-concept)
private static class JTextAreaWithExtendedRowHeight extends JTextArea{
private JTextAreaWithExtendedRowHeight( int rows, int columns ) {
super( rows, columns );
}
#Override
public FontMetrics getFontMetrics( Font font ) {
FontMetrics fontMetrics = super.getFontMetrics( font );
return new FontMetricsWrapper( font, fontMetrics );
}
}
The FontMetricsWrapper class basically delegates everything, except the getHeight method. In that method I added 10 to the result of the delegate
#Override
public int getHeight() {
//use +10 to make the difference obvious
return delegate.getHeight() + 10;
}
And this results in more row spacing (and a caret which is way too long, but that can probably be adjusted).
A little screenshot to illustrate this (not as nice as some of the other ones, but it shows that this approach might work):
Small disclaimer: this feels like an ugly hack and might result in unexpected issues. I do hope somebody comes with a better solution.
I personally prefer the solution StanislavL is proposing, but this gives you an alternative
That's a piece of code. It's not finished. Line spacing between wrapped lines is not implemented. You can get full source of WrappedPlainView or PlainView and add your code there to achieve desired line spacing
import javax.swing.*;
import javax.swing.plaf.basic.BasicTextAreaUI;
import javax.swing.text.*;
public class LineSpacingTextArea {
public static void main(String[] args) {
JTextArea ta=new JTextArea();
JFrame fr=new JFrame("Custom line spacing in JTextArea");
fr.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ta.setText("Line 1\nLine 2\nLong text to show how line spacing works");
ta.setLineWrap(true);
ta.setWrapStyleWord(true);
ta.setUI(new CustomTextAreaUI());
fr.add(new JScrollPane(ta));
fr.setSize(100,200);
fr.setLocationRelativeTo(null);
fr.setVisible(true);
}
static class CustomTextAreaUI extends BasicTextAreaUI {
public View create(Element elem) {
Document doc = elem.getDocument();
Object i18nFlag = doc.getProperty("i18n"/*AbstractDocument.I18NProperty*/);
if ((i18nFlag != null) && i18nFlag.equals(Boolean.TRUE)) {
// build a view that support bidi
return super.create(elem);
} else {
JTextComponent c = getComponent();
if (c instanceof JTextArea) {
JTextArea area = (JTextArea) c;
View v;
if (area.getLineWrap()) {
v = new CustomWrappedPlainView(elem, area.getWrapStyleWord());
} else {
v = new PlainView(elem);
}
return v;
}
}
return null;
}
}
static class CustomWrappedPlainView extends WrappedPlainView {
public CustomWrappedPlainView(Element elem, boolean wordWrap) {
super(elem, wordWrap);
}
protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
super.layoutMajorAxis(targetSpan, axis, offsets, spans);
int ls=spans[0];
for (int i=0; i<offsets.length; i++) {
offsets[i]+=i*ls;
}
}
}
}