Tetris example improvements

This commit is contained in:
Vadim Lopatin 2014-12-26 14:20:12 +03:00
parent af5a631051
commit a77a20e6e7
5 changed files with 222 additions and 176 deletions

View File

@ -1,14 +1,29 @@
module gui; module gui;
import dlangui.all;
import dlangui.widgets.popup;
import dlangui.graphics.drawbuf;
//import std.stdio;
import std.conv;
import std.utf;
import model; import model;
import dlangui.all;
/// game action codes
enum TetrisAction : int {
MoveLeft = 10000,
MoveRight,
RotateCCW,
FastDown,
Pause,
LevelUp,
}
const Action ACTION_MOVE_LEFT = (new Action(TetrisAction.MoveLeft, KeyCode.LEFT)).addAccelerator(KeyCode.KEY_A);
const Action ACTION_MOVE_RIGHT = (new Action(TetrisAction.MoveRight, KeyCode.RIGHT)).addAccelerator(KeyCode.KEY_D);
const Action ACTION_ROTATE = (new Action(TetrisAction.RotateCCW, KeyCode.UP)).addAccelerator(KeyCode.KEY_W);
const Action ACTION_FAST_DOWN = (new Action(TetrisAction.FastDown, KeyCode.SPACE)).addAccelerator(KeyCode.KEY_S);
const Action ACTION_PAUSE = (new Action(TetrisAction.Pause, KeyCode.ESCAPE)).addAccelerator(KeyCode.PAUSE);
const Action ACTION_LEVEL_UP = (new Action(TetrisAction.LevelUp, KeyCode.ADD)).addAccelerator(KeyCode.INS);
const Action[] CUP_ACTIONS = [ACTION_MOVE_LEFT, ACTION_MOVE_RIGHT, ACTION_ROTATE, ACTION_FAST_DOWN, ACTION_PAUSE, ACTION_LEVEL_UP];
/// about dialog
Widget createAboutWidget() Widget createAboutWidget()
{ {
LinearLayout res = new VerticalLayout(); LinearLayout res = new VerticalLayout();
@ -26,16 +41,24 @@ Widget createAboutWidget()
return res; return res;
} }
enum TetrisAction : int { /// Cup States
MoveLeft = 10000, enum CupState : int {
MoveRight, /// New figure appears
RotateCCW, NewFigure,
FastDown, /// Game is paused
Pause, Paused,
LevelUp, /// Figure is falling
FallingFigure,
/// Figure is hanging - pause between falling by one row
HangingFigure,
/// destroying complete rows
DestroyingRows,
/// falling after some rows were destroyed
FallingRows,
/// Game is over
GameOver,
} }
/// Cup widget /// Cup widget
class CupWidget : Widget { class CupWidget : Widget {
/// cup columns count /// cup columns count
@ -54,37 +77,21 @@ class CupWidget : Widget {
long _movementDuration; long _movementDuration;
/// When true, figure is falling down fast /// When true, figure is falling down fast
bool _fastDownFlag; bool _fastDownFlag;
/// animation helper for fade and movement in different states
AnimationHelper _animation; AnimationHelper _animation;
/// GameOver popup
private PopupWidget _gameOverPopup; private PopupWidget _gameOverPopup;
/// Status widget
/// Cup States private StatusWidget _status;
enum CupState : int { /// Current state
/// New figure appears
NewFigure,
/// Game is paused
Paused,
/// Figure is falling
FallingFigure,
/// Figure is hanging - pause between falling by one row
HangingFigure,
/// destroying complete rows
DestroyingRows,
/// falling after some rows were destroyed
FallingRows,
/// Game is over
GameOver,
}
protected CupState _state; protected CupState _state;
protected int _totalRowsDestroyed;
static const int[10] LEVEL_SPEED = [15000000, 10000000, 7000000, 6000000, 5000000, 4000000, 3500000, 3000000, 2500000, 2000000]; static const int[10] LEVEL_SPEED = [15000000, 10000000, 7000000, 6000000, 5000000, 4000000, 3500000, 3000000, 2500000, 2000000];
static const int RESERVED_ROWS = 5; // reserved for next figure static const int RESERVED_ROWS = 5; // reserved for next figure
/// set difficulty level 1..10 /// set difficulty level 1..10
void setLevel(int level) { void setLevel(int level) {
_level = level; _level = level;
@ -93,8 +100,10 @@ class CupWidget : Widget {
} }
static const int MIN_FAST_FALLING_INTERVAL = 600000; static const int MIN_FAST_FALLING_INTERVAL = 600000;
static const int ROWS_FALLING_INTERVAL = 600000;
static const int ROWS_FALLING_INTERVAL = 1200000;
/// change game state, init state animation when necessary
void setCupState(CupState state) { void setCupState(CupState state) {
int animationIntervalPercent = 100; int animationIntervalPercent = 100;
switch (state) { switch (state) {
@ -114,6 +123,8 @@ class CupWidget : Widget {
animationIntervalPercent = 50; animationIntervalPercent = 50;
break; break;
default: default:
// no animation for other states
animationIntervalPercent = 0;
break; break;
} }
_state = state; _state = state;
@ -128,18 +139,14 @@ class CupWidget : Widget {
invalidate(); invalidate();
} }
/// returns true is widget is being animated - need to call animate() and redraw void addScore(int score) {
override @property bool animating() { _score += score;
switch (_state) { _status.setScore(_score);
case CupState.NewFigure: }
case CupState.FallingFigure:
case CupState.HangingFigure: /// returns true if figure is in falling - movement state
case CupState.DestroyingRows: @property bool falling() {
case CupState.FallingRows: return _state == CupState.FallingFigure;
return true;
default:
return false;
}
} }
/// Turn on / off fast falling down /// Turn on / off fast falling down
@ -167,6 +174,8 @@ class CupWidget : Widget {
return true; return true;
} }
static const int[] NEXT_LEVEL_SCORE = [0, 20, 50, 100, 200, 350, 500, 750, 1000, 1500, 2000];
/// try start next figure /// try start next figure
protected void nextFigure() { protected void nextFigure() {
if (!_cup.dropNextFigure()) { if (!_cup.dropNextFigure()) {
@ -177,6 +186,8 @@ class CupWidget : Widget {
_gameOverPopup = window.showPopup(popupWidget, this); _gameOverPopup = window.showPopup(popupWidget, this);
} else { } else {
setCupState(CupState.NewFigure); setCupState(CupState.NewFigure);
if (_level < 10 && _totalRowsDestroyed >= NEXT_LEVEL_SCORE[_level])
setLevel(_level + 1); // level up
} }
} }
@ -214,6 +225,8 @@ class CupWidget : Widget {
break; break;
case CupState.DestroyingRows: case CupState.DestroyingRows:
int rowsDestroyed = _cup.destroyFullRows(); int rowsDestroyed = _cup.destroyFullRows();
_totalRowsDestroyed += rowsDestroyed;
_status.setRowsDestroyed(_totalRowsDestroyed);
int scorePerRow = 0; int scorePerRow = 0;
for (int i = 0; i < rowsDestroyed; i++) { for (int i = 0; i < rowsDestroyed; i++) {
scorePerRow += 10; scorePerRow += 10;
@ -245,14 +258,23 @@ class CupWidget : Widget {
} }
} }
/// animates window; interval is time left from previous draw, in hnsecs (1/10000000 of second) /// start new game
override void animate(long interval) { void newGame() {
_animation.animate(interval); setLevel(1);
if (_animation.finished) { init(_cols, _rows);
onAnimationFinished(); _cup.dropNextFigure();
setCupState(CupState.NewFigure);
if (window && _gameOverPopup) {
window.removePopup(_gameOverPopup);
_gameOverPopup = null;
} }
_score = 0;
_status.setScore(0);
_totalRowsDestroyed = 0;
_status.setRowsDestroyed(0);
} }
/// init cup
void init(int cols, int rows) { void init(int cols, int rows) {
_cup.init(cols, rows); _cup.init(cols, rows);
_cols = cols; _cols = cols;
@ -321,6 +343,31 @@ class CupWidget : Widget {
} }
} }
//=================================================================================================
// Overrides of Widget methods
/// returns true is widget is being animated - need to call animate() and redraw
override @property bool animating() {
switch (_state) {
case CupState.NewFigure:
case CupState.FallingFigure:
case CupState.HangingFigure:
case CupState.DestroyingRows:
case CupState.FallingRows:
return true;
default:
return false;
}
}
/// animates window; interval is time left from previous draw, in hnsecs (1/10000000 of second)
override void animate(long interval) {
_animation.animate(interval);
if (_animation.finished) {
onAnimationFinished();
}
}
/// Draw widget at its position to buffer /// Draw widget at its position to buffer
override void onDraw(DrawBuf buf) { override void onDraw(DrawBuf buf) {
super.onDraw(buf); super.onDraw(buf);
@ -387,15 +434,6 @@ class CupWidget : Widget {
} }
} }
}
/// Measure widget according to desired width and height constraints. (Step 1 of two phase layout).
override void measure(int parentWidth, int parentHeight) {
/// fixed size 350 x 550
measuredContent(parentWidth, parentHeight, 350, 550);
}
@property bool falling() {
return _state == CupState.FallingFigure;
} }
/// override to handle specific actions /// override to handle specific actions
@ -428,54 +466,30 @@ class CupWidget : Widget {
} }
} }
void addScore(int score) { /// Measure widget according to desired width and height constraints. (Step 1 of two phase layout).
_score += score; override void measure(int parentWidth, int parentHeight) {
_status.setScore(_score); measuredContent(parentWidth, parentHeight, parentWidth * 3 / 5, parentHeight);
} }
/// start new game
void newGame() {
setLevel(1);
_score = 0;
init(_cols, _rows);
_cup.dropNextFigure();
setCupState(CupState.NewFigure);
if (window && _gameOverPopup) {
window.removePopup(_gameOverPopup);
_gameOverPopup = null;
}
_status.setScore(_score);
}
private StatusWidget _status;
this(StatusWidget status) { this(StatusWidget status) {
super("CUP"); super("CUP");
this._status = status; this._status = status;
layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT).layoutWeight(3); layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT).layoutWeight(3).setState(State.Default).focusable(true).padding(Rect(20, 20, 20, 20));
setState(State.Default);
//backgroundColor = 0xC0808080; _cols = 10;
padding(Rect(20, 20, 20, 20)); _rows = 18;
_cols = 11;
_rows = 15;
newGame(); newGame();
focusable = true; focusable = true;
acceleratorMap.add( [ acceleratorMap.add(CUP_ACTIONS);
(new Action(TetrisAction.MoveLeft, KeyCode.LEFT)).addAccelerator(KeyCode.KEY_A),
(new Action(TetrisAction.MoveRight, KeyCode.RIGHT)).addAccelerator(KeyCode.KEY_D),
(new Action(TetrisAction.RotateCCW, KeyCode.UP)).addAccelerator(KeyCode.KEY_W),
(new Action(TetrisAction.FastDown, KeyCode.SPACE)).addAccelerator(KeyCode.KEY_S),
(new Action(TetrisAction.Pause, KeyCode.ESCAPE)).addAccelerator(KeyCode.PAUSE),
(new Action(TetrisAction.LevelUp, KeyCode.ADD)).addAccelerator(KeyCode.INS),
]);
} }
} }
/// Panel to show game status /// Panel to show game status
class StatusWidget : VerticalLayout { class StatusWidget : VerticalLayout {
private TextWidget _level; private TextWidget _level;
private TextWidget _rowsDestroyed;
private TextWidget _score; private TextWidget _score;
private CupWidget _cup; private CupWidget _cup;
void setCup(CupWidget cup) { void setCup(CupWidget cup) {
@ -483,26 +497,27 @@ class StatusWidget : VerticalLayout {
} }
TextWidget createTextWidget(dstring str, uint color) { TextWidget createTextWidget(dstring str, uint color) {
TextWidget res = new TextWidget(null, str); TextWidget res = new TextWidget(null, str);
res.layoutWidth(FILL_PARENT).alignment(Align.Center); res.layoutWidth(FILL_PARENT).alignment(Align.Center).fontSize(25).textColor(color);
res.fontSize(30);
res.textColor(color);
return res; return res;
} }
this() { this() {
super("CUP_STATUS"); super("CUP_STATUS");
//backgroundColor = 0xC080FF80;
addChild(new VSpacer()); addChild(new VSpacer());
addChild(new ImageWidget(null, "tetris_logo_big")); addChild((new ImageWidget(null, "tetris_logo_big")).layoutWidth(FILL_PARENT).alignment(Align.Center));
addChild(new VSpacer()); addChild(new VSpacer());
addChild(createTextWidget("Level:"d, 0x008000)); addChild(createTextWidget("Level:"d, 0x008000));
addChild((_level = createTextWidget(""d, 0x008000))); addChild((_level = createTextWidget(""d, 0x008000)));
addChild(new VSpacer()); addChild(new VSpacer());
addChild(createTextWidget("Rows:"d, 0x202080));
addChild((_rowsDestroyed = createTextWidget(""d, 0x202080)));
addChild(new VSpacer());
addChild(createTextWidget("Score:"d, 0x800000)); addChild(createTextWidget("Score:"d, 0x800000));
addChild((_score = createTextWidget(""d, 0x800000))); addChild((_score = createTextWidget(""d, 0x800000)));
addChild(new VSpacer()); addChild(new VSpacer());
addChild(new VSpacer()); addChild(new VSpacer());
layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT).layoutWeight(2);
padding(Rect(20, 20, 20, 20)); layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT).layoutWeight(2).padding(Rect(20, 20, 20, 20));
} }
void setLevel(int level) { void setLevel(int level) {
@ -513,17 +528,16 @@ class StatusWidget : VerticalLayout {
_score.text = toUTF32(to!string(score)); _score.text = toUTF32(to!string(score));
} }
/// Measure widget according to desired width and height constraints. (Step 1 of two phase layout). void setRowsDestroyed(int rows) {
override void measure(int parentWidth, int parentHeight) { _rowsDestroyed.text = toUTF32(to!string(rows));
super.measure(parentWidth, parentHeight);
/// fixed size 350 x 550
measuredContent(parentWidth, parentHeight, 150, 550);
} }
override bool handleAction(const Action a) { override bool handleAction(const Action a) {
return _cup.handleAction(a); return _cup.handleAction(a);
} }
} }
/// Cup page: cup widget + status widget
class CupPage : HorizontalLayout { class CupPage : HorizontalLayout {
CupWidget _cup; CupWidget _cup;
StatusWidget _status; StatusWidget _status;
@ -539,13 +553,13 @@ class CupPage : HorizontalLayout {
/// Measure widget according to desired width and height constraints. (Step 1 of two phase layout). /// Measure widget according to desired width and height constraints. (Step 1 of two phase layout).
override void measure(int parentWidth, int parentHeight) { override void measure(int parentWidth, int parentHeight) {
super.measure(parentWidth, parentHeight); super.measure(parentWidth, parentHeight);
/// fixed size 350 x 550 /// fixed size
measuredContent(parentWidth, parentHeight, 500, 550); measuredContent(parentWidth, parentHeight, 550, 550);
} }
} }
//FrameLayout //
class GameWidget : HorizontalLayout { class GameWidget : FrameLayout {
CupPage _cupPage; CupPage _cupPage;
this() { this() {
@ -555,9 +569,4 @@ class GameWidget : HorizontalLayout {
//showChild(_cupPage.id, Visibility.Invisible, true); //showChild(_cupPage.id, Visibility.Invisible, true);
backgroundImageId = "tx_fabric.tiled"; backgroundImageId = "tx_fabric.tiled";
} }
/// Measure widget according to desired width and height constraints. (Step 1 of two phase layout).
override void measure(int parentWidth, int parentHeight) {
super.measure(parentWidth, parentHeight);
measuredContent(parentWidth, parentHeight, 500, 550);
}
} }

View File

@ -19,10 +19,9 @@ import dlangui.all;
import model; import model;
import gui; import gui;
/// Required for Windows platform: DMD cannot find WinMain if it's in library
mixin APP_ENTRY_POINT; mixin APP_ENTRY_POINT;
/// entry point for dlangui based application /// entry point for dlangui based application
extern (C) int UIAppMain(string[] args) { extern (C) int UIAppMain(string[] args) {
@ -50,14 +49,10 @@ extern (C) int UIAppMain(string[] args) {
// load theme from file "theme_default.xml" // load theme from file "theme_default.xml"
Platform.instance.uiTheme = "theme_default"; Platform.instance.uiTheme = "theme_default";
//drawableCache.get("tx_fabric.tiled");
// create window // create window
Window window = Platform.instance.createWindow("DLangUI: Tetris game example", null, WindowFlag.Modal); Window window = Platform.instance.createWindow("DLangUI: Tetris game example"d, null, WindowFlag.Modal);
GameWidget game = new GameWidget(); window.mainWidget = new GameWidget();
window.mainWidget = game;
window.windowIcon = drawableCache.getImage("dtetris-logo1"); window.windowIcon = drawableCache.getImage("dtetris-logo1");

View File

@ -2,6 +2,28 @@ module model;
import std.random : uniform; import std.random : uniform;
/// Cell codes
enum : int {
WALL = -1,
EMPTY = 0,
FIGURE1,
FIGURE2,
FIGURE3,
FIGURE4,
FIGURE5,
FIGURE6,
FIGURE7,
}
/// Orientations
enum : int {
ORIENTATION0,
ORIENTATION90,
ORIENTATION180,
ORIENTATION270
}
/// Cell offset /// Cell offset
struct FigureCell { struct FigureCell {
// horizontal offset // horizontal offset
@ -22,8 +44,9 @@ struct FigureShape {
int extent; int extent;
/// upper y coordinate - initial Y offset to place figure to cup /// upper y coordinate - initial Y offset to place figure to cup
int y0; int y0;
this(int[2] c1, int[2] c2, int[2] c3, int[2] c4) { /// Init cells (cell 0 is [0,0])
cells[0] = FigureCell(c1); this(int[2] c2, int[2] c3, int[2] c4) {
cells[0] = FigureCell([0, 0]);
cells[1] = FigureCell(c2); cells[1] = FigureCell(c2);
cells[2] = FigureCell(c3); cells[2] = FigureCell(c3);
cells[3] = FigureCell(c4); cells[3] = FigureCell(c4);
@ -37,6 +60,7 @@ struct FigureShape {
} }
} }
/// Figure data - shapes for 4 orientations
struct Figure { struct Figure {
FigureShape[4] shapes; // by orientation FigureShape[4] shapes; // by orientation
this(FigureShape[4] v) { this(FigureShape[4] v) {
@ -44,71 +68,69 @@ struct Figure {
} }
} }
const Figure[6] FIGURES = [ /// All shapes
const Figure[7] FIGURES = [
// FIGURE1 ===========================================
// ## #### // ## ####
// 00## 00## // 00## 00##
// ## // ##
Figure([FigureShape([0, 0], [1, 0], [1, 1], [0, -1]), Figure([FigureShape([1, 0], [1, 1], [0, -1]),
FigureShape([0, 0], [0, 1], [-1, 1], [1, 0]), FigureShape([0, 1], [-1, 1], [1, 0]),
FigureShape([0, 0], [1, 0], [1, 1], [0, -1]), FigureShape([1, 0], [1, 1], [0, -1]),
FigureShape([0, 0], [0, 1], [-1, 1], [1, 0])]), FigureShape([0, 1], [-1, 1], [1, 0])]),
// FIGURE2 ===========================================
// ## #### // ## ####
// 00## ##00 // 00## ##00
// ## // ##
Figure([FigureShape([0, 0], [1, 0], [0, 1], [1, 1]), Figure([FigureShape([1, 0], [0, 1], [1, 1]),
FigureShape([0, 0], [0, 1], [1, 1], [-1, 0]), FigureShape([0, 1], [1, 1], [-1, 0]),
FigureShape([0, 0], [1, 0], [0, 1], [1, 1]), FigureShape([1, 0], [0, 1], [1, 1]),
FigureShape([0, 0], [0, 1], [1, 1], [-1, 0])]), FigureShape([0, 1], [1, 1], [-1, 0])]),
// FIGURE3 ===========================================
// ## ## #### // ## ## ####
// ##00## 00 ##00## 00 // ##00## 00 ##00## 00
// ## #### ## // ## #### ##
Figure([FigureShape([0, 0], [1, 0], [-1,0], [-1,-1]), Figure([FigureShape([1, 0], [-1,0], [-1,-1]),
FigureShape([0, 0], [0, 1], [0,-1], [ 1,-1]), FigureShape([0, 1], [0,-1], [ 1,-1]),
FigureShape([0, 0], [1, 0], [-1,0], [1, 1]), FigureShape([1, 0], [-1,0], [1, 1]),
FigureShape([0, 0], [0, 1], [-1,1], [0,-1])]), FigureShape([0, 1], [-1,1], [0,-1])]),
// FIGURE4 ===========================================
// #### ## ## // #### ## ##
// ##00## 00 ##00## 00 // ##00## 00 ##00## 00
// ## ## #### // ## ## ####
Figure([FigureShape([0, 0], [1, 0], [-1,0], [ 1, 1]), Figure([FigureShape([1, 0], [-1,0], [ 1,-1]),
FigureShape([0, 0], [0, 1], [0,-1], [ 1, 1]), FigureShape([0, 1], [0,-1], [ 1, 1]),
FigureShape([0, 0], [1, 0], [-1,0], [-1, 1]), FigureShape([1, 0], [-1,0], [-1, 1]),
FigureShape([0, 0], [0, 1], [-1,-1], [0, -1])]), FigureShape([0, 1], [-1,-1], [0, -1])]),
// FIGURE5 ===========================================
// #### // ####
// 00## // 00##
// //
Figure([FigureShape([0, 0], [1, 0], [0, 1], [ 1, 1]), Figure([FigureShape([1, 0], [0, 1], [ 1, 1]),
FigureShape([0, 0], [1, 0], [0, 1], [ 1, 1]), FigureShape([1, 0], [0, 1], [ 1, 1]),
FigureShape([0, 0], [1, 0], [0, 1], [ 1, 1]), FigureShape([1, 0], [0, 1], [ 1, 1]),
FigureShape([0, 0], [1, 0], [0, 1], [ 1, 1])]), FigureShape([1, 0], [0, 1], [ 1, 1])]),
// FIGURE6 ===========================================
// ## // ##
// ## // ##
// 00 ##00#### // 00 ##00####
// ## // ##
Figure([FigureShape([0, 0], [0, 1], [0, 2], [ 0,-1]), Figure([FigureShape([0, 1], [0, 2], [ 0,-1]),
FigureShape([0, 0], [1, 0], [2, 0], [-1, 0]), FigureShape([1, 0], [2, 0], [-1, 0]),
FigureShape([0, 0], [0, 1], [0, 2], [ 0,-1]), FigureShape([0, 1], [0, 2], [ 0,-1]),
FigureShape([0, 0], [1, 0], [2, 0], [-1, 0])]), FigureShape([1, 0], [2, 0], [-1, 0])]),
// FIGURE7 ===========================================
// ## ## ##
// ##00## 00## ##00## ##00
// ## ## ##
Figure([FigureShape([1, 0], [-1,0], [ 0,-1]),
FigureShape([0, 1], [0,-1], [ 1, 0]),
FigureShape([1, 0], [-1,0], [ 0, 1]),
FigureShape([0, 1], [0,-1], [-1, 0])]),
]; ];
enum : int { /// colors for different figure types
WALL = -1, const uint[7] _figureColors = [0xC00000, 0x80A000, 0xA00080, 0x0000C0, 0x800020, 0x408000, 0x204000];
EMPTY = 0,
FIGURE1,
FIGURE2,
FIGURE3,
FIGURE4,
FIGURE5,
FIGURE6,
}
enum : int {
ORIENTATION0,
ORIENTATION90,
ORIENTATION180,
ORIENTATION270
}
const uint[6] _figureColors = [0xFF0000, 0xA0A000, 0xA000A0, 0x0000FF, 0x800000, 0x408000];
/// Figure type, orientation and position container /// Figure type, orientation and position container
struct FigurePosition { struct FigurePosition {
@ -289,13 +311,15 @@ struct Cup {
return false; return false;
} }
/// random next figure
void genNextFigure() { void genNextFigure() {
_nextFigure.index = uniform(FIGURE1, FIGURE6 + 1); _nextFigure.index = uniform(FIGURE1, FIGURE7 + 1);
_nextFigure.orientation = ORIENTATION0; _nextFigure.orientation = ORIENTATION0;
_nextFigure.x = _cols / 2; _nextFigure.x = _cols / 2;
_nextFigure.y = _rows - _nextFigure.shape.extent + 1; _nextFigure.y = _rows - _nextFigure.shape.extent + 1;
} }
/// New figure: put it on top of cup
bool dropNextFigure() { bool dropNextFigure() {
if (_nextFigure.empty) if (_nextFigure.empty)
genNextFigure(); genNextFigure();
@ -412,6 +436,7 @@ struct Cup {
return cellGroup(col, row) > 0; return cellGroup(col, row) > 0;
} }
/// returns true if next figure is generated
@property bool hasNextFigure() { @property bool hasNextFigure() {
return !_nextFigure.empty; return !_nextFigure.empty;
} }

View File

@ -48,7 +48,8 @@ module dlangui.all;
public import dlangui.core.logger; public import dlangui.core.logger;
public import dlangui.core.types; public import dlangui.core.types;
public import dlangui.platforms.common.platform; public import dlangui.core.i18n;
public import dlangui.core.files;
public import dlangui.graphics.images; public import dlangui.graphics.images;
public import dlangui.widgets.widget; public import dlangui.widgets.widget;
public import dlangui.widgets.controls; public import dlangui.widgets.controls;
@ -61,6 +62,11 @@ public import dlangui.widgets.editors;
public import dlangui.widgets.grid; public import dlangui.widgets.grid;
public import dlangui.widgets.tree; public import dlangui.widgets.tree;
public import dlangui.widgets.combobox; public import dlangui.widgets.combobox;
public import dlangui.widgets.popup;
public import dlangui.graphics.fonts; public import dlangui.graphics.fonts;
public import dlangui.core.i18n; public import dlangui.graphics.drawbuf;
public import dlangui.core.files; public import dlangui.platforms.common.platform;
// some useful imports from Phobos
public import std.conv : to;
public import std.utf : toUTF32, toUTF8;

View File

@ -140,6 +140,10 @@ class Action {
@property Accelerator[] accelerators() { @property Accelerator[] accelerators() {
return _accelerators; return _accelerators;
} }
/// returs const array of accelerators
@property const(Accelerator)[] accelerators() const {
return _accelerators;
}
/// returns text description for first accelerator of action; null if no accelerators /// returns text description for first accelerator of action; null if no accelerators
@property dstring acceleratorText() { @property dstring acceleratorText() {
if (_accelerators.length < 1) if (_accelerators.length < 1)
@ -217,6 +221,13 @@ struct ActionMap {
_map[acc] = a; _map[acc] = a;
} }
} }
/// Add array of actions
void add(const Action[] items) {
foreach(a; items) {
foreach(acc; a.accelerators)
_map[acc] = a.clone;
}
}
/// Add action /// Add action
void add(Action a) { void add(Action a) {
foreach(acc; a.accelerators) foreach(acc; a.accelerators)