I want to change the colors of background button and the color of text onfocus
How can I make it?
class RoundedRectField extends Field {
// Layout values
private static final int CURVE_X = 12; // X-axis inset of curve
private static final int CURVE_Y = 12; // Y-axis inset of curve
private static final int MARGIN = 2; // Space within component boundary
// Static colors
private static final int TEXT_COLOR = 0xFFFFFF; // White
private static final int BORDER_COLOR = 0xFF8000; // dark gray
private static final int BACKGROUND_COLOR = 0xFFFFFF; // White
private static final int TEXT_COLOR_selected = 0xFF6DB6;
private static final int BORDER_COLOR_selected = 0xFF8000;
private static final int BACKGROUND_COLOR_selected = 0xCCCCCC;
boolean _focus = false;
private static String text_button;
// Point types array for rounded rectangle. Each point type
// corresponds to one of the colors in the colors array. The
// space marks the division between points on the top half of
// the rectangle and those on the bottom.
private static final byte[] PATH_POINT_TYPES = {
Graphics.CURVEDPATH_END_POINT,
Graphics.CURVEDPATH_QUADRATIC_BEZIER_CONTROL_POINT,
Graphics.CURVEDPATH_END_POINT, Graphics.CURVEDPATH_END_POINT,
Graphics.CURVEDPATH_QUADRATIC_BEZIER_CONTROL_POINT,
Graphics.CURVEDPATH_END_POINT,
Graphics.CURVEDPATH_END_POINT,
Graphics.CURVEDPATH_QUADRATIC_BEZIER_CONTROL_POINT,
Graphics.CURVEDPATH_END_POINT, Graphics.CURVEDPATH_END_POINT,
Graphics.CURVEDPATH_QUADRATIC_BEZIER_CONTROL_POINT,
Graphics.CURVEDPATH_END_POINT, };
// Colors array for rounded rectangle gradient. Each color corresponds
// to one of the points in the point types array. Top light, bottom black.
private static final int[] PATH_GRADIENT = { 0xFF8000, 0xFF8000, 0xFF8000,
0xFF8000, 0xFF8000, 0xFF8000,
0xFC0500, 0xFC0500, 0xFC0500, 0xFC0500, 0xFC0500, 0xFC0500 };
// Center our readonly field in the space we're given.
public RoundedRectField(String text_button) {
super(FIELD_HCENTER | FIELD_VCENTER | READONLY);
this.text_button = text_button;
}
// This field in this demo has a fixed height.
public int getPreferredHeight() {
return 70;
}
// This field in this demo has a fixed width.
public int getPreferredWidth() {
return 240;
}
// When layout is requested, return our height and width.
protected void layout(int width, int height) {
setExtent(getPreferredWidth(), getPreferredHeight());
}
// When painting is requested, do it ourselves.
protected void paint(Graphics g) {
// Clear this area to white background, fully opaque.
g.clear();
g.setGlobalAlpha(255);
g.setBackgroundColor(BACKGROUND_COLOR);
// Drawing within our margin.
int width = getPreferredWidth() - (MARGIN * 2);
int height = getPreferredHeight() - (MARGIN * 2);
// Compute paths for the rounded rectangle. The 1st point (0) is on
// the left
// side, right where the curve in the top left corner starts. So the
// top left
// corner is point 1. These points correspond to our static arrays.
int[] xPts = { 0, 0, CURVE_X, width - CURVE_X, width, width, width,
width, width - CURVE_X, CURVE_X, 0, 0 };
int[] yPts = { CURVE_Y, 0, 0, 0, 0, CURVE_Y, height - CURVE_Y,
height, height, height, height, height - CURVE_Y };
// Draw the gradient fill.
g.drawShadedFilledPath(xPts, yPts, PATH_POINT_TYPES, PATH_GRADIENT,
null);
// Draw a rounded rectangle for the outline.
// I think that drawRoundRect looks better than drawPathOutline.
g.setColor(BORDER_COLOR);
g.drawRoundRect(0, 0, width, height, CURVE_X * 2, CURVE_Y * 2);
// Place some text in the center.
Font font = Font.getDefault().derive(Font.PLAIN, 9, Ui.UNITS_pt);
int textWidth = font.getAdvance(text_button);
int textHeight = font.getHeight();
g.setColor(TEXT_COLOR);
g.setFont(font);
g.drawText(text_button, (width / 2) - (textWidth / 2) - MARGIN,
(height / 2) - (textHeight / 2) - MARGIN);
}
protected void onFocus(int direction) {
_focus = true;
Dialog.alert("dcd");
invalidate();
super.onFocus(direction);
}
protected void onUnfocus() {
_focus = false;
invalidate();
super.onUnfocus();
}
}
You can do it several ways. One popular way is to provide custom focus drawing in the paint() method, which you already override.
You should be able to do this (I'm assuming you declared the _selected colors for the focused state):
if (isFocus()) {
g.setBackgroundColor(BACKGROUND_COLOR_selected);
else {
g.setBackgroundColor(BACKGROUND_COLOR);
}
...
if (isFocus()) {
g.setColor(TEXT_COLOR_selected);
} else {
g.setColor(TEXT_COLOR);
}
Those lines go in paint(), right where you are currently calling g.setBackgroundColor and g.setColor(TEXT_COLOR).
Then, you would override drawFocus() and do nothing, since your focus drawing is handled in paint():
protected void drawFocus(Graphics graphics, boolean on) {
// override superclass implementation and do nothing
}
Finally, you need to make your Field focusable, in order to ever receive focus. You can do so like this:
public RoundedRectField(String text_button) {
super(FIELD_HCENTER | FIELD_VCENTER | FOCUSABLE);
this.text_button = text_button;
}
If you need the field to be dynamically focusable (sometimes focusable, or sometimes not focusable), then you could implement this method:
public boolean isFocusable() {
But, if the field is always focusable, then using the FOCUSABLE flag in your constructor will work. I tested this out, and I saw the text color change with focus (on a OS 5.0 9550).
Related
I have a very strange issue. In the following code at 261 in the TouchActioNTypeCancelled i want to call a Method which should set the CroppingRect.Rect to my bindable Property testImage. The Strange thing: When testIamge is a normal Property, not bindable, everything works fine. I can move the croppign rectangle with all corners in the view. If i now change the Proeprty to a bindable property when i start movign the rectangle on one corner in the app i can move the rectangle only from the opposite corners again and if i press the opposite corner, the rectangle buggs back into its original position. I don't understand how this can happen. I did not yet consume the Property in the view since i wanted to check first if it works without consuming it.
This is the way it works with a npormal property. In fact, this is, how i want it to work with a bindable proeprty as well:
Workign example(only with normal property)
This, however, is how it works with the bindable property currently:
Not working example(Bindable property)
public static readonly BindableProperty testImageProperty =
BindableProperty.Create(nameof(testImage), typeof(SKRect), typeof(SKRect));
public SKRect testImage
{
get
{
return (SKRect)GetValue(testImageProperty);
}
set
{
SetValue(testImageProperty, value);
}
}
//public SKRect testImage { get; set; }
void OnTouchEffectTouchAction(object sender, TouchActionEventArgs args)
{
int i = 0;
SKPoint pixelLocation = ConvertToPixel(args.Location);
SKPoint bitmapLocation = inverseBitmapMatrix.MapPoint(pixelLocation);
switch (args.Type)
{
case TouchActionType.Pressed:
// Convert radius to bitmap/cropping scale
float radius = inverseBitmapMatrix.ScaleX * RADIUS;
// Find corner that the finger is touching
int cornerIndex = croppingRect.HitTest(bitmapLocation, radius);
if (cornerIndex != -1 && !touchPoints.ContainsKey(args.Id))
{
TouchPoint touchPoint = new TouchPoint
{
CornerIndex = cornerIndex,
Offset = bitmapLocation - croppingRect.Corners[cornerIndex]
};
touchPoints.Add(args.Id, touchPoint);
}
break;
case TouchActionType.Moved:
if (touchPoints.ContainsKey(args.Id))
{
TouchPoint touchPoint = touchPoints[args.Id];
croppingRect.MoveCorner(touchPoint.CornerIndex,
bitmapLocation - touchPoint.Offset);
InvalidateSurface();
}
break;
case TouchActionType.Released:
case TouchActionType.Cancelled:
if (touchPoints.ContainsKey(args.Id))
{
SetMap();
touchPoints.Remove(args.Id);
}
break;
}
}
private void SetMap()
{
testImage = croppingRect.Rect;
Console.WriteLine("test");
}
I have a simple HBOX control which uses USE_COMPUTED_SIZE in Pref-Height, hence the size is all calculated and adjusted by the controls inside, which are a couple VBOX.
The issue comes when I try to add a new Pane as a children to the HBOX and draw a vertical line from top to bottom of the HBOX, so I write my line:
int startX = 5;
int startY = 0;
int endX = 5;
Line line = new Line(startX,startY,endX,hbox.getHeight());
Here, I need the hbox.getHeight(), but surprise: it is =-1, because it is using USE_COMPUTED_SIZE. So, how can I get the real (computed) value of hbox.getHeight()?
Not tested, but I think you can do something like:
public class HBoxWithLine extends HBox {
// Example of configurable property:
private final DoubleProperty lineOffset = new SimpleDoubleProperty(5);
public DoubleProperty lineOffsetProperty() {
return lineOffset ;
}
public final double getLineOffset() {
return lineOffsetProperty().get();
}
public final void setLineOffset(double lineOffset) {
lineOffsetProperty().set(lineOffset);
}
private final Line line = new Line();
public HBoxWithLine() {
getChildren().add(line);
// request layout when offset is invalidated:
lineOffset.addListener(obs -> requestLayout());
}
#Override
protected void layoutChildren() {
line.setStartX(getLineOffset());
line.setEndX(getLineOffset());
line.setStartY(0);
line.setEndY(getHeight());
super.layoutChildren();
}
}
Now you can just create a HBoxWithLine and add (additional) child nodes to it, set it's pref width and height to either fixed values, or Region.USE_COMPUTED_SIZE, etc., and it should just work.
How can I center the keyboard on the bottom of the screen?
I just have the sizes of the keyboard/PopupWindow after the board is already shown. Before calling show() every function returns 0.0 for the asked width. I could set the position correctly if I just knew the width before.
The keyboard size might change later, that's why I can't do it with a set size.
I am using the fx-onscreen-keyboard
My little service:
public class KeyboardService {
private double screenWidth = Screen.getPrimary().getVisualBounds().getWidth();
private double screenHight = Screen.getPrimary().getVisualBounds().getHeight();
private double keyboardPosX = 0.0;
private double keyboardPosY = 0.0;
private KeyBoardPopup keyboardPopup;
public KeyboardService() {
keyboardPopup = KeyBoardPopupBuilder.create().initLocale(Locale.GERMAN).build();
keyboardPopup.setAutoHide(true);
keyboardPopup.setConsumeAutoHidingEvents(false);
keyboardPopup.getKeyBoard().setScale(2.5);
keyboardPopup.getKeyBoard().setLayer(DefaultLayer.DEFAULT);
keyboardPopup.getKeyBoard().setOnKeyboardCloseButton((e) -> {
keyboardPopup.hide();
});
}
public void showKeyboard(Node node){
keyboardPosX = (screenWidth - keyboardPopup.getWidth())/2;
//keyboardPosX = (screenWidth - keyboardPopup.getKeyBoard().getWidth())/2;
keyboardPosY = screenHight;
keyboardPopup.show(node, keyboardPosX, keyboardPosY);
}}
The width of the keyboard is defined when the popup that holds it is laid out, what happens right after you call show.
The easy way to do this is by listening to the widthProperty of the KeyBoardPopup, to get the new value, and then moving the window of the popup accordingly.
This will do:
public KeyboardService() {
keyboardPopup = KeyBoardPopupBuilder.create().initLocale(Locale.GERMAN).build();
keyboardPopup.setAutoHide(true);
keyboardPopup.setConsumeAutoHidingEvents(false);
keyboardPopup.getKeyBoard().setScale(2.5);
keyboardPopup.getKeyBoard().setLayer(DefaultLayer.DEFAULT);
keyboardPopup.getKeyBoard().setOnKeyboardCloseButton((e) -> {
keyboardPopup.hide();
});
// listen to width changes and center
keyboardPopup.widthProperty().addListener((obs, ov, nv) -> {
keyboardPosX = (screenWidth - nv.doubleValue()) / 2d;
keyboardPopup.getScene().getWindow().setX(keyboardPosX);
});
}
public void showKeyboard(Node node) {
keyboardPosX = (screenWidth - keyboardPopup.getWidth())/2;
keyboardPosY = screenHeight;
keyboardPopup.show(node, keyboardPosX, keyboardPosY);
}
The text button draws normally (no errors or warnings) but it doesn't respond to mouse clicks. The situation is the same on Desktop and Android build. I've read and followed every topic about it with no results. What am I doing wrong?
Maybe I need to set something in stage object?
Here's my MenuScreen class code:
public class MenuScreen implements Screen {
private OrthographicCamera camera;
private BitmapFont font;
private TextureAtlas menuGraphics;
Stage stage;
TextButton button;
TextButtonStyle buttonStyle;
Skin skin;
public MenuScreen(Game game)
{
//create satage object
stage = new Stage();
//create font object
font = new BitmapFont(Gdx.files.internal("data/arial-15.fnt"),false);
font.setColor(Color.RED);
//load buttons texture atlas and create Skin object
menuGraphics = new TextureAtlas( Gdx.files.internal( "buttons/buttons.pack" ) );
skin = new Skin();
skin.addRegions(menuGraphics);
// Store the default libgdx font under the name "defaultFont".
skin.add("defaultFont",font);
//Set button style
buttonStyle = new TextButtonStyle();
buttonStyle.up = skin.newDrawable("yellow");
buttonStyle.down = skin.newDrawable("orange");
buttonStyle.checked = skin.newDrawable("orange");
buttonStyle.over = skin.newDrawable("orange");
buttonStyle.font = skin.getFont("defaultFont");
skin.add("default", buttonStyle);
//assign button style
button=new TextButton("PLAY",buttonStyle);
button.setPosition(200, 200);
button.setSize(200,200);
//add button to stage
stage.addActor(button);
//add click listener to the button
button.addListener(new ClickListener() {
public void clicked(InputEvent event, float x, float y) {
button.setText("Starting new game");
Gdx.app.log("MenuScreen", "clicked button");
}
});
Gdx.app.log("MenuScreen", "create");
}
#Override
public void resize(int width, int height )
{
float aspectRatio = (float) width / (float) height;
camera = new OrthographicCamera(640, 360);
camera.translate(320,180);
camera.update();
stage.setViewport(new FillViewport(640, 360, camera));
stage.getViewport().setCamera(camera);
Gdx.app.log( "MenuScreen", "Resizing screen to: " + width + " x " + height );
}
#Override
public void show() {
Gdx.input.setInputProcessor(stage);
float w = Gdx.graphics.getWidth();
float h = Gdx.graphics.getHeight();
resize((int)w,(int)h);
Gdx.app.log( "MenuScreen", "Show screen code" );
}
#Override
public void render(float delta)
{
Gdx.gl.glClearColor( 0f, 1f, 0f, 1f );
Gdx.gl.glClear( GL20.GL_COLOR_BUFFER_BIT );
stage.act( delta );
// draw the actors
stage.draw();
}
#Override
public void dispose()
{
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void hide() {
}
}
I Finally solved it!
Additional line in resize function did the trick:
stage.getViewport().update(width, height);
I realized that the problem was caused by setting the Camera and Viewport in the resize function. When I removed that code (an adjusted the button position to be visible with standard stage viewport) the button started working.
But I wanted to use camera and viewport. Reading about stage resizing in LibGDX documantation (https://github.com/libgdx/libgdx/wiki/Scene2d) I found out that after setting camera, the viewport needs to be updated, otherwise the actor boundries are not recalculated. So I added the viewport update line in resize function and it started working!
The new resize function looks like that:
#Override
public void resize(int width, int height )
{
float aspectRatio = (float) width / (float) height;
camera = new OrthographicCamera(640, 360);
camera.translate(320,180);
camera.update();
stage.setViewport(new FillViewport(640, 360, camera));
stage.getViewport().setCamera(camera);
stage.getViewport().update(width, height);
Gdx.app.log( "MenuScreen", "Resizing screen to: " + width + " x " + height );
}
add implements ApplicationListener to your class
public MenuScreen implements ApplicationListener
hope this helps.
This is about as basic as it gets. I don't want to use an image file. Rather, I want to programmatically draw a circle and blit it to a surface (as they say in pygame).
I tried to follow the "Using CanvasLayers" example here:
https://developers.google.com/playn/devguide/rendering
From my game class:
// Surface
SurfaceLayer surface;
// Background
int width = 640;
int height = 480;
//ImageLayer bgLayer;
CanvasImage bgImage;
Canvas canvas;
// Circle
CanvasImage circleImage;
//ImageLayer circleLayer;
int circleRadius = 20;
int circleX = 0;
int circleY = 0;
#Override
public void init() {
// create a surface
surface = graphics().createSurfaceLayer(width, height);
graphics().rootLayer().add(surface);
// create a solid background
// http://code.google.com/p/playn101/source/browse/core/src/main/java/playn101/core/J.java#81
bgImage = graphics().createImage(width, height);
canvas = bgImage.canvas();
canvas.setFillColor(0xff87ceeb);
canvas.fillRect(0, 0, width, height);
//bgLayer = graphics().createImageLayer(bgImage);
//graphics().rootLayer().add(bgLayer);
// create a circle
circleImage = graphics().createImage(circleRadius, circleRadius);
canvas = circleImage.canvas();
canvas.setFillColor(0xff0000eb);
canvas.fillCircle(circleX, circleY, circleRadius);
//circleLayer = graphics().createImageLayer(circleImage);
//graphics().rootLayer().add(circleLayer);
}
#Override
public void paint(float alpha) {
// the background automatically paints itself, so no need to do anything
// here!
surface.clear(0);
surface.drawImage(bgImage, 0, 0);
surface.drawImage(circleImage, 100, 100);
}
But I get a blank window in Java and Eclipse complains:
The method drawImage(CanvasImage, int, int) is undefined for the type
SurfaceLayer
That, however, is the way it is used in the example at the link.
If the code you provided even compiles, then you are using some very old version of PlayN.
Update to PlayN 1.1.1 and fix the compilation errors that result, and your code will work fine.
The following is your code updated to work with PlayN 1.1.1:
private SurfaceLayer surface;
private CanvasImage bgImage;
private CanvasImage circleImage;
#Override
public void init() {
// create a surface
int width = graphics().width(), height = graphics().height();
surface = graphics().createSurfaceLayer(width, height);
graphics().rootLayer().add(surface);
// create a solid background
bgImage = graphics().createImage(width, height);
Canvas canvas = bgImage.canvas();
canvas.setFillColor(0xff87ceeb);
canvas.fillRect(0, 0, width, height);
// create a circle
int circleRadius = 20;
int circleX = 0;
int circleY = 0;
circleImage = graphics().createImage(circleRadius, circleRadius);
canvas = circleImage.canvas();
canvas.setFillColor(0xff0000eb);
canvas.fillCircle(circleX, circleY, circleRadius);
}
#Override
public void paint(float alpha) {
Surface s = surface.surface();
s.clear();
s.drawImage(bgImage, 0, 0);
s.drawImage(circleImage, 100, 100);
}
If you really intend to make a game using this approach, you should use ImmediateLayer not SurfaceLayer. ImmediateLayer will issue your drawImage, etc. calls directly against the framebuffer. SurfaceLayer will make an off-screen framebuffer and render everything into that and then copy that off-screen framebuffer to the main framebuffer every frame (in addition to the double buffering naturally performed by OpenGL, etc.), resulting in a needless copy of your entire screen.
Your code looks fine to me...
set the canvasTransform,
canvas.setTransform(1, 0, 0, 1, 0, 0);
and please for testing purposes make the bg bigger
graphics().createImage(circleRadius, circleRadius); (400,400)