/******************************************************************************* * Copyright 2011 See AUTHORS file. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. ******************************************************************************/ package com.badlogic.gdx.scenes.scene2d.ui; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.g2d.BitmapFont; import com.badlogic.gdx.graphics.g2d.BitmapFont.TextBounds; import com.badlogic.gdx.graphics.g2d.BitmapFontCache; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.scenes.scene2d.InputEvent; import com.badlogic.gdx.scenes.scene2d.InputListener; import com.badlogic.gdx.scenes.scene2d.Stage; import com.badlogic.gdx.scenes.scene2d.Touchable; import com.badlogic.gdx.scenes.scene2d.utils.Align; import com.badlogic.gdx.scenes.scene2d.utils.Drawable; /** * A table that can be dragged and act as a modal window. The top padding is used as the window's title height. * <p> * The preferred size of a window is the preferred size of the title text and the children as layed out by the table. * After adding children to the window, it can be convenient to call {@link #pack()} to size the window to the size of * the children. * * @author Nathan Sweet */ public class Window extends Table { private WindowStyle style; private String title; private BitmapFontCache titleCache; boolean isMovable = true, isModal; final Vector2 dragOffset = new Vector2(); boolean dragging; private int titleAlignment = Align.center; boolean keepWithinStage = true; public Window(String title, Skin skin) { this(title, skin.get(WindowStyle.class)); setSkin(skin); } public Window(String title, Skin skin, String styleName) { this(title, skin.get(styleName, WindowStyle.class)); setSkin(skin); } public Window(String title, WindowStyle style) { if (title == null) throw new IllegalArgumentException("title cannot be null."); this.title = title; setTouchable(Touchable.enabled); setClip(true); setStyle(style); setWidth(150); setHeight(150); setTitle(title); addCaptureListener(new InputListener() { public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) { toFront(); return false; } }); addListener(new InputListener() { public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) { if (button == 0) { dragging = isMovable && getHeight() - y <= getPadTop() && y < getHeight() && x > 0 && x < getWidth(); dragOffset.set(x, y); } return dragging || isModal; } public void touchDragged(InputEvent event, float x, float y, int pointer) { if (!dragging) return; translate(x - dragOffset.x, y - dragOffset.y); } public boolean mouseMoved(InputEvent event, float x, float y) { return isModal; } public boolean scrolled(InputEvent event, float x, float y, int amount) { return isModal; } public boolean keyDown(InputEvent event, int keycode) { return isModal; } public boolean keyUp(InputEvent event, int keycode) { return isModal; } public boolean keyTyped(InputEvent event, char character) { return isModal; } }); } public void setStyle(WindowStyle style) { if (style == null) throw new IllegalArgumentException("style cannot be null."); this.style = style; setBackground(style.background); titleCache = new BitmapFontCache(style.titleFont); titleCache.setColor(style.titleFontColor); if (title != null) setTitle(title); invalidateHierarchy(); } /** * Returns the window's style. Modifying the returned style may not have an effect until * {@link #setStyle(WindowStyle)} is called. */ public WindowStyle getStyle() { return style; } public void draw(SpriteBatch batch, float parentAlpha) { Stage stage = getStage(); if (keepWithinStage && getParent() == stage.getRoot()) { float parentWidth = stage.getWidth(); float parentHeight = stage.getHeight(); if (getX() < 0) setX(0); if (getRight() > parentWidth) setX(parentWidth - getWidth()); if (getY() < 0) setY(0); if (getTop() > parentHeight) setY(parentHeight - getHeight()); } super.draw(batch, parentAlpha); } protected void drawBackground(SpriteBatch batch, float parentAlpha) { if (style.stageBackground != null) { Color color = getColor(); batch.setColor(color.r, color.g, color.b, color.a * parentAlpha); Stage stage = getStage(); Vector2 position = stageToLocalCoordinates(Vector2.tmp.set(0, 0)); Vector2 size = stageToLocalCoordinates(Vector2.tmp2.set(stage.getWidth(), stage.getHeight())); style.stageBackground.draw(batch, getX() + position.x, getY() + position.y, getX() + size.x, getY() + size.y); } super.drawBackground(batch, parentAlpha); // Draw the title without the batch transformed or clipping applied. float x = getX(), y = getY() + getHeight(); TextBounds bounds = titleCache.getBounds(); if ((titleAlignment & Align.left) != 0) x += getPadLeft(); else if ((titleAlignment & Align.right) != 0) x += getWidth() - bounds.width - getPadRight(); else x += (getWidth() - bounds.width) / 2; if ((titleAlignment & Align.top) == 0) { if ((titleAlignment & Align.bottom) != 0) y -= getPadTop() - bounds.height; else y -= (getPadTop() - bounds.height) / 2; } titleCache.setColor(Color.tmp.set(getColor()).mul(style.titleFontColor)); titleCache.setPosition((int) x, (int) y); titleCache.draw(batch, parentAlpha); } public Actor hit(float x, float y, boolean touchable) { Actor hit = super.hit(x, y, touchable); if (hit == null && isModal && (!touchable || getTouchable() == Touchable.enabled)) return this; return hit; } public void setTitle(String title) { this.title = title; titleCache.setMultiLineText(title, 0, 0); } public String getTitle() { return title; } /** * @param titleAlignment * {@link Align} */ public void setTitleAlignment(int titleAlignment) { this.titleAlignment = titleAlignment; } public void setMovable(boolean isMovable) { this.isMovable = isMovable; } public void setModal(boolean isModal) { this.isModal = isModal; } public void setKeepWithinStage(boolean keepWithinStage) { this.keepWithinStage = keepWithinStage; } public boolean isDragging() { return dragging; } public float getPrefWidth() { return Math.max(super.getPrefWidth(), titleCache.getBounds().width + getPadLeft() + getPadRight()); } /** * The style for a window, see {@link Window}. * * @author Nathan Sweet */ static public class WindowStyle { /** Optional. */ public Drawable background; public BitmapFont titleFont; /** Optional. */ public Color titleFontColor = new Color(1, 1, 1, 1); /** Optional. */ public Drawable stageBackground; public WindowStyle() { } public WindowStyle(BitmapFont titleFont, Color titleFontColor, Drawable background) { this.background = background; this.titleFont = titleFont; this.titleFontColor.set(titleFontColor); } public WindowStyle(WindowStyle style) { this.background = style.background; this.titleFont = style.titleFont; this.titleFontColor = new Color(style.titleFontColor); } } }