[server] add module

This commit is contained in:
2020-03-25 16:24:53 +03:00
parent 821ddbb2a7
commit 8e3b9e2830
99 changed files with 70 additions and 4 deletions

View File

@@ -0,0 +1,99 @@
package ru.m.puzzlez.view;
import haxe.Timer;
import hw.view.frame.FrameSwitcher;
import hw.view.frame.FrameView;
import hw.view.popup.ConfirmView;
import promhx.Promise;
import ru.m.puzzlez.core.Game;
import ru.m.puzzlez.core.GameEvent;
import ru.m.puzzlez.core.GameState;
import ru.m.puzzlez.core.IGame;
import ru.m.puzzlez.render.IRender;
import ru.m.puzzlez.storage.GameStorage;
import ru.m.puzzlez.storage.SettingsStorage;
import ru.m.puzzlez.view.popup.BackgroundPopup;
import ru.m.puzzlez.view.popup.PreviewPopup;
@:template class GameFrame extends FrameView<GameState> {
public static var ID = "game";
@:view private var render:IRender;
private var game:IGame;
@:provide var switcher:FrameSwitcher;
@:provide var storage:GameStorage;
@:provide var settings:SettingsStorage;
private var saveTimer:Timer;
public function new() {
super(ID);
}
override public function onShow(state:GameState):Void {
onHide();
game = new Game(state);
game.signal.connect(render.onGameEvent);
game.signal.connect(onGameEvent);
render.signal.connect(game.signal.emit);
game.start();
}
override public function onHide():Void {
if (saveTimer != null) {
save();
}
if (game != null) {
render.signal.disconnect(game.signal.emit);
game.stop();
game.dispose();
game = null;
}
}
private function toSave():Void {
if (saveTimer == null) {
saveTimer = Timer.delay(save, 5000);
}
}
private function save():Void {
if (saveTimer != null) {
saveTimer.stop();
saveTimer = null;
}
if (game != null) {
storage.save(game.state);
}
}
private function onGameEvent(event:GameEvent):Void {
switch event {
case START(_) | ACTION(PART_PUT(_, _)):
toSave();
case _:
}
}
private function showPreview():Void {
PreviewPopup.instance.showPreview(game.state);
}
private function choiseBackground():Void {
BackgroundPopup.instance.choise(settings.background).then(background -> {
if (background != null) {
settings.background = background;
render.toRedraw();
}
});
}
private function back():Void {
(game.state.status == COMPLETE ? Promise.promise(true) : ConfirmView.confirm("Exit?"))
.then(result -> {
if (result) {
switcher.change(ImageListFrame.ID, storage.statusSource(game.state.status));
}
});
}
}

View File

@@ -0,0 +1,41 @@
---
style: frame
layout:
$type: hw.view.layout.HorizontalLayout
views:
- $type: hw.view.group.VGroupView
geometry.height: 100%
geometry.padding: 5
layout.margin: 10
views:
- $type: hw.view.form.ButtonView
style: icon.image
+onPress: ~showPreview()
- $type: hw.view.SpriteView
geometry.stretch: true
- $type: hw.view.form.ToggleButtonView
style: icon.lock
on: true
+onPress: |
~button -> {
render.manager.locked = !render.manager.locked;
cast(button, hw.view.form.ToggleButtonView).on = !render.manager.locked;
}
- $type: hw.view.form.ButtonView
style: icon.restore
+onPress: ~render.manager.reset()
- $type: hw.view.form.ButtonView
style: icon.setting
geometry.margin.top: 30
+onPress: ~choiseBackground()
- id: render
$type: ru.m.puzzlez.render.Render
geometry.width: 100%
geometry.height: 100%
- $type: hw.view.form.ButtonView
style: icon.close
geometry.position: absolute
geometry.hAlign: right
geometry.vAlign: top
geometry.margin: 5
+onPress: ~back()

View File

@@ -0,0 +1,51 @@
package ru.m.puzzlez.view;
import hw.view.form.ButtonView;
import hw.view.form.LabelView;
import hw.view.frame.FrameSwitcher;
import hw.view.frame.FrameView;
import ru.m.puzzlez.core.ImageListSource;
import ru.m.puzzlez.FileUtil;
import ru.m.puzzlez.source.FileSource;
import ru.m.puzzlez.storage.GameStorage;
import ru.m.puzzlez.storage.ImageStorage;
import ru.m.puzzlez.view.common.ImageDataList;
@:template class ImageListFrame extends FrameView<ImageListSource> {
public static var ID = "image_list";
@:view("header") var headerView:LabelView;
@:view("images") var imagesView:ImageDataList;
@:view var select:ButtonView;
@:provide var gameStorage:GameStorage;
@:provide var imageStorage:ImageStorage;
@:provide var switcher:FrameSwitcher;
public function new() {
super(ID);
}
override public function onShow(data:ImageListSource):Void {
imagesView.reset();
if (data != null) {
headerView.text = data.title;
// ToDo:
select.visible = Std.is(data.source, FileSource);
imagesView.page.filter = data.filter;
imagesView.source = data.source;
imagesView.refresh();
}
}
private function selectFile():Void {
FileUtil.browse().then((data:FileContent) -> {
var fileSource:FileSource = cast imageStorage.sources.get(FileSource.ID);
fileSource.append(data.content).then(_ -> imagesView.refresh());
});
}
private function back():Void {
switcher.change(StartFrame.ID);
}
}

View File

@@ -0,0 +1,20 @@
---
style: frame
views:
- id: header
$type: hw.view.form.LabelView
style: label.header
- id: images
$type: ru.m.puzzlez.view.common.ImageDataList
geometry.stretch: true
- id: select
$type: hw.view.form.ButtonView
text: Select...
+onPress: ~selectFile()
visible: false
- $type: hw.view.form.ButtonView
text: Back
geometry.position: absolute
geometry.hAlign: right
geometry.vAlign: top
+onPress: ~back()

View File

@@ -0,0 +1,108 @@
package ru.m.puzzlez.view;
import hw.view.form.LabelView;
import flash.text.TextFormatAlign;
import hw.view.geometry.HAlign;
import hw.view.geometry.Position;
import hw.view.geometry.VAlign;
import hw.view.group.IGroupView;
import hw.view.IView;
import hw.view.text.TextView;
import promhx.Promise;
enum State {
NONE;
LOADING;
ERROR(error:Dynamic);
}
class LoadingWrapper {
public var promise(null, set):Promise<Dynamic>;
private function set_promise(value:Promise<Dynamic>):Promise<Dynamic> {
state = LOADING;
value
.then(_ -> state = NONE)
.catchError(error -> state = ERROR(error));
return value;
}
public var state(default, set):State;
private function set_state(value:State):State {
if (state != value) {
state = value;
switch state {
case NONE:
overlay = null;
case LOADING:
overlay = loadingView;
case ERROR(error):
L.e("wrapper", "", error);
cast(errorView, TextView).text = Std.string(error);
overlay = errorView;
}
}
return state;
}
private var view:IGroupView;
private var overlay(default, set):IView<Dynamic>;
private function set_overlay(value:IView<Dynamic>):IView<Dynamic> {
if (overlay != null && view.containsView(overlay)) {
view.removeView(overlay);
}
overlay = value;
if (overlay != null) {
view.addView(overlay);
}
return overlay;
}
private var loadingView(get, null):IView<Dynamic>;
private function get_loadingView():IView<Dynamic> {
if (loadingView == null) {
loadingView = buildLoadingView();
}
return loadingView;
}
private var errorView(get, null):IView<Dynamic>;
private function get_errorView():IView<Dynamic> {
if (errorView == null) {
errorView = buildErrorView();
}
return errorView;
}
public function new(view:IGroupView) {
this.view = view;
}
private function buildLoadingView():IView<Dynamic> {
var view = new LabelView();
view.geometry.position = ABSOLUTE;
view.geometry.hAlign = CENTER;
view.geometry.vAlign = MIDDLE;
view.text = "Loading...";
return view;
}
private function buildErrorView():IView<Dynamic> {
var view = new TextView();
view.geometry.position = ABSOLUTE;
view.geometry.hAlign = CENTER;
view.geometry.vAlign = MIDDLE;
view.geometry.stretch = true;
view.style = "text.error";
return view;
}
public function reset():Void {
overlay = null;
}
}

View File

@@ -0,0 +1,63 @@
package ru.m.puzzlez.view;
import hw.geom.IntPoint;
import hw.view.data.DataView;
import hw.view.form.ToggleButtonView;
import hw.view.frame.FrameSwitcher;
import hw.view.frame.FrameView;
import ru.m.puzzlez.core.GameUtil;
import ru.m.puzzlez.core.Id;
import ru.m.puzzlez.storage.ImageStorage;
import ru.m.puzzlez.view.common.PresetView;
@:template class PresetFrame extends FrameView<ImageId> {
public static var ID = "preset";
@:view("image") var imageView:PresetView;
@:view("sizes") var sizesView:DataView<IntPoint, ToggleButtonView>;
@:provide var imageStorage:ImageStorage;
@:provide var switcher:FrameSwitcher;
private var imageId:ImageId;
public function new() {
super(ID);
sizesView.data = [
new IntPoint(3, 2),
new IntPoint(4, 3),
new IntPoint(5, 4),
new IntPoint(6, 5),
new IntPoint(7, 6),
new IntPoint(8, 7),
new IntPoint(10, 8),
];
}
private function factory(index:Int, size:IntPoint):ToggleButtonView {
var result = new ToggleButtonView();
result.text = '${size.x}x${size.y}';
return result;
}
private function selectSize(size:IntPoint):Void {
imageView.state = GameUtil.buildState(GameUtil.buildPreset(imageId, size.x, size.y));
for (i in 0...sizesView.dataViews.length) {
sizesView.dataViews[i].on = size == sizesView.data[i];
}
}
override public function onShow(data:ImageId):Void {
super.onShow(data);
imageId = data;
selectSize(sizesView.data[sizesView.data.length - 1]);
}
private function start():Void {
switcher.change(GameFrame.ID, imageView.state);
}
private function back():Void {
switcher.change(ImageListFrame.ID);
}
}

View File

@@ -0,0 +1,36 @@
---
style: frame
layout.margin: 10
views:
- $type: hw.view.form.LabelView
text: Puzzle configure
style: label.header
- $type: hw.view.group.HGroupView
geometry.width: 100%
layout.hAlign: center
layout.vAlign: middle
views:
- id: sizes
$type: hw.view.data.DataView
layout:
$type: hw.view.layout.TailLayout
hAlign: center
stretch: true
margin: 5
factory: ~factory
+onDataSelect: ~selectSize
- $type: hw.view.form.ButtonView
style: button.active
geometry.margin.left: 15
text: Start
+onPress: ~start()
- id: image
$type: ru.m.puzzlez.view.common.PresetView
geometry.stretch: true
geometry.margin: 15
- $type: hw.view.form.ButtonView
text: Back
geometry.position: absolute
geometry.hAlign: right
geometry.vAlign: top
+onPress: ~back()

View File

@@ -0,0 +1,34 @@
package ru.m.puzzlez.view;
import flash.events.KeyboardEvent;
import flash.ui.Keyboard;
import hw.view.form.ButtonView;
import hw.view.frame.FrameSwitcher;
import hw.view.group.VGroupView;
import hw.app.App;
@:template class PuzzlezAppView extends VGroupView {
@:view("switcher") var switcherView:FrameSwitcher;
@:view("fullscreen") var fullscreenButton:ButtonView;
@:provide static var switcher:FrameSwitcher;
@:provide static var app:App;
public function new() {
super();
fullscreenButton.visible = app.fullScreenSupport;
app.fullScreenSignal.connect(fs -> fullscreenButton.style = 'icon.${fs ? "compress" : "expand"}');
switcher = switcherView;
switcher.change(StartFrame.ID);
stage.addEventListener(KeyboardEvent.KEY_DOWN, (event:KeyboardEvent) -> {
switch event.keyCode {
case Keyboard.ESCAPE:
switcher.change(StartFrame.ID);
case Keyboard.F:
app.fullScreen = !app.fullScreen;
case _:
}
});
}
}

View File

@@ -0,0 +1,20 @@
---
views:
- $type: hw.view.frame.FrameSwitcher
id: switcher
geometry.stretch: true
style: dark
factory:
_start_: {$class: ru.m.puzzlez.view.StartFrame}
_image_list_: {$class: ru.m.puzzlez.view.ImageListFrame}
_preset_: {$class: ru.m.puzzlez.view.PresetFrame}
_game_: {$class: ru.m.puzzlez.view.GameFrame}
- id: fullscreen
$type: hw.view.form.ButtonView
geometry.position: absolute
geometry.hAlign: right
geometry.vAlign: bottom
geometry.margin: 10
style: icon.expand
+onPress: ~_ -> app.fullScreen = !app.fullScreen
visible: false

View File

@@ -0,0 +1,81 @@
package ru.m.puzzlez.view;
import hw.view.data.DataView;
import hw.view.form.ButtonView;
import hw.view.frame.FrameSwitcher;
import hw.view.frame.FrameView;
import ru.m.data.IDataSource;
import ru.m.pixabay.PixabayApi;
import ru.m.puzzlez.core.GameState.GameStatus;
import ru.m.puzzlez.core.ImageListSource;
import ru.m.puzzlez.source.AssetSource;
import ru.m.puzzlez.source.FileSource;
import ru.m.puzzlez.source.PixabaySource;
import ru.m.puzzlez.storage.GameStorage;
import ru.m.puzzlez.storage.ImageStorage;
import ru.m.update.Updater;
@:template class StartFrame extends FrameView<Dynamic> {
public static var ID = "start";
@:view var sources:DataView<ImageListSource, ButtonView>;
@:view("load") var loadButton:ButtonView;
@:view("complete") var completeButton:ButtonView;
@:view("update") var updateButton:ButtonView;
@:provide var storage:ImageStorage;
@:provide var switcher:FrameSwitcher;
@:provide var gameStorage:GameStorage;
@:provide static var appUpdater:Updater;
public function new() {
super(ID);
var data:Array<ImageListSource> = [];
data.push({title: "Assets", source: storage.sources.get(AssetSource.ID)});
data.push({title: "Files", source: storage.sources.get(FileSource.ID)});
var pixabay:PixabaySource = cast storage.sources.get(PixabaySource.ID);
for (type in AbstractEnumTools.getValues(PixabayCategory)) {
data.push(pixabay.categorySource(type));
}
sources.data = data;
}
private function refresh():Void {
var startedRequest:Page = {index: 0, count: 0, filter: ["status" => STARTED]};
gameStorage.getIndexPage(startedRequest).then(page -> {
var total = page.total;
loadButton.text = 'Resume (${total})';
loadButton.disabled = total == 0;
});
var completeRequest:Page = {index: 0, count: 0, filter: ["status" => COMPLETE]};
gameStorage.getIndexPage(completeRequest).then(page -> {
var total = page.total;
completeButton.text = 'Complete (${total})';
completeButton.disabled = total == 0;
});
}
override public function onShow(data:Dynamic):Void {
refresh();
appUpdater.check().then((info:Null<PackageInfo>) -> {
if (info != null) {
updateButton.visible = true;
updateButton.text = 'Update ${info.version}';
}
});
}
private function sourceViewFactory(index:Int, source:ImageListSource):ButtonView {
var result = new ButtonView();
result.text = source.title;
return result;
}
private function load(source:ImageListSource):Void {
switcher.change(ImageListFrame.ID, source);
}
private function games(status:GameStatus):Void {
switcher.change(ImageListFrame.ID, gameStorage.statusSource(status));
}
}

View File

@@ -0,0 +1,57 @@
---
style: frame
views:
- $type: hw.view.group.HGroupView
geometry.hAlign: center
geometry.margin.top: 15
layout.vAlign: middle
views:
- $type: hw.view.ImageView
image: $a:image:resources/icon.png
stretch: false
fillType: CONTAIN
geometry.width: 96
geometry.height: 96
- $type: hw.view.form.LabelView
text: $r:text:app.name
font.size: 50
- id: sources
$type: hw.view.data.DataView
layout:
$type: hw.view.layout.TailLayout
rowSize: 5
margin: 10
vAlign: middle
geometry.stretch: true
factory: ~sourceViewFactory
+onDataSelect: ~load
geometry.margin: 5
overflow.y: scroll
- $type: hw.view.group.HGroupView
geometry.width: 100%
layout.vAlign: middle
layout.margin: 10
views:
- id: load
$type: hw.view.form.ButtonView
text: Load
+onPress: ~games('started')
- id: complete
$type: hw.view.form.ButtonView
text: Complete
+onPress: ~games('complete')
- $type: hw.view.SpriteView
geometry.width: 100%
- $type: hw.view.form.LabelView
text: $r:text:app.version
geometry.position: absolute
geometry.hAlign: right
geometry.vAlign: top
- id: update
$type: hw.view.form.ButtonView
style: button.active
geometry.position: absolute
geometry.hAlign: left
geometry.vAlign: top
+onPress: ~appUpdater.download()
visible: false

View File

@@ -0,0 +1,92 @@
package ru.m.puzzlez.view.common;
import hw.view.data.DataView;
import hw.view.form.ToggleButtonView;
import hw.view.frame.FrameSwitcher;
import hw.view.group.VGroupView;
import hw.view.popup.ConfirmView;
import ru.m.data.IDataSource;
import ru.m.puzzlez.core.Id;
import ru.m.puzzlez.source.FileSource;
import ru.m.puzzlez.storage.GameStorage;
import ru.m.puzzlez.storage.ImageStorage;
import ru.m.puzzlez.view.common.PuzzleImageView;
@:template class ImageDataList extends VGroupView {
public var source:IDataIndex<ImageId>;
public var page:Page;
@:view("images") var imagesView:ActionDataView<ImageId, PuzzleImageView, Action>;
@:view("paginator") var paginatorView:PaginatorView;
private var loading:LoadingWrapper;
@:provide var switcher:FrameSwitcher;
@:provide var gameStorage:GameStorage;
@:provide var imageStorage:ImageStorage;
public var data(default, set):DataPage<ImageId>;
private function set_data(value:DataPage<ImageId>):DataPage<ImageId> {
data = value;
imagesView.data = data.data;
paginatorView.page = data;
return data;
}
public function new() {
super();
loading = new LoadingWrapper(imagesView);
page = {index: 0, count: 6, order: [{key: "date", reverse: true}]};
}
private function pageFactory(index:Int, value:Int):ToggleButtonView {
var result = new ToggleButtonView();
result.text = '${value}';
result.on = data.page.index == value;
return result;
}
private function start(imageId:ImageId):Void {
gameStorage.get(imageId).then(state -> {
if (state != null) {
switcher.change(GameFrame.ID, state);
} else {
switcher.change(PresetFrame.ID, imageId);
}
});
}
private function onAction(imageId:ImageId, action:Action):Void {
switch action {
case REMOVE:
var fileSource:FileSource = cast imageStorage.sources.get(FileSource.ID);
if (fileSource != null) {
ConfirmView.confirm("Delete image?").then(result -> {
if (result) {
fileSource.remove(imageId).then(_ -> refresh());
}
});
}
case CLEAN:
ConfirmView.confirm("Delete state?").then(result -> {
if (result) {
gameStorage.delete(imageId).then(_ -> refresh());
}
});
}
}
public function refresh():Void {
loading.promise = source.getIndexPage(page).then(data -> this.data = data);
}
public function reset():Void {
page.index = 0;
data = {
page: page,
total: 0,
data: [],
};
}
}

View File

@@ -0,0 +1,23 @@
---
views:
- id: images
$type: hw.view.data.ActionDataView
layout:
$type: hw.view.layout.TailLayout
rowSize: 3
margin: 5
vAlign: middle
geometry.stretch: true
factory: ~ru.m.puzzlez.view.common.PuzzleImageView.factory
+onDataSelect: ~start
+onDataAction: ~onAction
geometry.margin: 5
overflow.y: scroll
- id: paginator
$type: ru.m.puzzlez.view.common.PaginatorView
geometry.width: 100%
+onPageSelect: |
~(index) -> {
page.index = index;
refresh();
}

View File

@@ -0,0 +1,82 @@
package ru.m.puzzlez.view.common;
import hw.signal.Signal;
import hw.view.data.DataView;
import hw.view.form.ToggleButtonView;
import hw.view.layout.TailLayout;
import ru.m.data.IDataSource.DataPage;
enum PaginatorAction {
START;
JUMP(distance:Int);
INDEX(index:Int);
END;
}
class PaginatorButtonView extends ToggleButtonView {
public var action(default, set):PaginatorAction;
private function set_action(value:PaginatorAction):PaginatorAction {
action = value;
text = switch action {
case INDEX(index): Std.string(index);
case x: Std.string(x);
}
return action;
}
public static function factory(index:Int, action:PaginatorAction):PaginatorButtonView {
var result = new PaginatorButtonView();
result.action = action;
return result;
}
}
class PaginatorView extends DataView<PaginatorAction, PaginatorButtonView> {
public var page(default, set):DataPage<Dynamic>;
public var onPageSelect(default, null):Signal<Int>;
private function set_page(value:DataPage<Dynamic>):DataPage<Dynamic> {
page = value;
refresh();
return page;
}
public function new() {
super(new TailLayout());
layout.margin = 3;
onPageSelect = new Signal();
factory = PaginatorButtonView.factory;
onDataSelect.connect(onAction);
}
private function onAction(action:PaginatorAction):Void {
onPageSelect.emit(switch action {
case START: 0;
case INDEX(index): index;
case JUMP(distance): page.page.index + distance;
case END: Std.int((page.total - 1) / page.page.count);
});
}
private function refresh():Void {
var total:Int = Std.int((page.total - 1) / page.page.count);
var start:Int = Std.int(Math.max(page.page.index - 2, 0));
var end:Int = Std.int(Math.min(page.page.index + 2, total));
var data = [for (index in start...end + 1) INDEX(index)];
if (start > 0) {
data.unshift(START);
}
if (end < total) {
data.push(END);
}
this.data = data;
for (button in dataViews) {
button.on = switch button.action {
case INDEX(index): index == page.page.index;
case x: false;
}
}
}
}

View File

@@ -0,0 +1,85 @@
package ru.m.puzzlez.view.common;
import flash.display.BitmapData;
import flash.display.Graphics;
import flash.display.Shape;
import hw.view.group.GroupView;
import ru.m.puzzlez.core.GameState;
import ru.m.puzzlez.render.RenderUtil;
import ru.m.puzzlez.storage.ImageStorage;
class PresetView extends GroupView {
@:provide var imageStorage:ImageStorage;
public var scale(get, set):Float;
private function get_scale():Float {
return table.scaleX;
}
private function set_scale(value:Float):Float {
var result = table.scaleX = table.scaleY = value;
table.x = (width - state.preset.imageRect.width * value) / 2;
table.y = (height - state.preset.imageRect.height * value) / 2;
return result;
}
public var state(default, set):GameState;
private function set_state(value:GameState):GameState {
state = value;
this.image = null;
table.graphics.clear();
loading.promise = imageStorage.resolve(state.preset.imageId).then(image -> {
this.image = RenderUtil.cropImage(image, state.preset.imageRect);
toRedraw();
toUpdate();
});
return state;
}
private var loading:LoadingWrapper;
public function new() {
super();
table = new Shape();
content.addChild(table);
loading = new LoadingWrapper(this);
}
private var table:Shape;
private var image:BitmapData;
override public function redraw():Void {
if (state == null || image == null) {
return;
}
var preset = state.preset;
var partWidth = preset.imageRect.width / preset.grid.width;
var partHeight = preset.imageRect.height / preset.grid.height;
var graphics:Graphics = table.graphics;
graphics.clear();
graphics.beginBitmapFill(image, null, false, true);
graphics.drawRect(0, 0, preset.imageRect.width, preset.imageRect.height);
graphics.endFill();
for (part in state.parts) {
var rect = part.rect.clone();
rect.x = part.gridX * part.rect.width;
rect.y = part.gridY * part.rect.height;
var path = RenderUtil.builder.build(rect, part.bounds);
for (value in RenderUtil.borderSettings) {
graphics.lineStyle(1, value.color, value.opacity);
path.move(value.offset.x, value.offset.y).draw(graphics);
graphics.lineStyle();
}
}
}
override public function update():Void {
super.update();
if (state != null) {
scale = Math.min(width / state.preset.imageRect.width, height / state.preset.imageRect.height);
}
}
}

View File

@@ -0,0 +1,78 @@
package ru.m.puzzlez.view.common;
import hw.view.data.DataView;
import hw.view.form.ButtonView;
import hw.view.form.LabelView;
import hw.view.group.GroupView;
import hw.view.ImageView;
import ru.m.puzzlez.core.GameUtil;
import ru.m.puzzlez.core.Id;
import ru.m.puzzlez.source.FileSource;
import ru.m.puzzlez.storage.GameStorage;
import ru.m.puzzlez.storage.ImageStorage;
enum Action {
CLEAN;
REMOVE;
}
@:template class PuzzleImageView extends GroupView {
public var imageId(default, set):ImageId;
private function set_imageId(value:ImageId):ImageId {
if (imageId != value) {
imageId = value;
loading.promise = imageStorage.resolve(imageId, true).then(data -> imageView.image = data);
}
return imageId;
}
public var text(default, set):String;
private function set_text(value:String):String {
if (text != value) {
text = value;
labelView.text = text;
}
return text;
}
@:view("image") var imageView:ImageView;
@:view("label") var labelView:LabelView;
@:view("clean") var cleanButton:ButtonView;
@:view("remove") var removeButton:ButtonView;
@:provide static var imageStorage:ImageStorage;
@:provide static var gameStorage:GameStorage;
private var loading:LoadingWrapper;
public function new() {
super();
cleanButton.visible = false;
removeButton.visible = false;
loading = new LoadingWrapper(this);
}
private function emit(action:Action):Void {
var dataView:ActionDataView<Dynamic, PuzzleImageView, Action> = Std.instance(parent, ActionDataView);
if (dataView != null) {
var index = dataView.dataViews.indexOf(this);
dataView.onDataAction.emit(dataView.data[index], action);
}
}
public static function factory(index:Int, imageId:ImageId):PuzzleImageView {
var result = new PuzzleImageView();
result.imageId = imageId;
gameStorage.get(imageId).then(state -> {
if (state != null) {
var progress = GameUtil.calcProgress(state);
result.text = '${progress.complete}/${progress.total}';
result.cleanButton.visible = true;
} else if (imageId.source == FileSource.ID) {
result.removeButton.visible = true;
}
});
return result;
}
}

View File

@@ -0,0 +1,20 @@
---
style: view
views:
- id: image
$type: hw.view.ImageView
geometry.stretch: true
stretch: false
fillType: COVER
- id: label
$type: hw.view.form.LabelView
- id: remove
$type: hw.view.form.ButtonView
propagation: false
style: icon.control.small.close.red
+onPress: ~emit(Action.REMOVE)
- id: clean
$type: hw.view.form.ButtonView
propagation: false
style: icon.control.small.close.orange
+onPress: ~emit(Action.CLEAN)

View File

@@ -0,0 +1,94 @@
package ru.m.puzzlez.view.popup;
import hw.color.Color;
import hw.view.data.DataView;
import hw.view.form.ButtonView;
import hw.view.popup.PopupView;
import hw.view.skin.Skin;
import hw.view.SpriteView;
import hw.view.utils.DrawUtil;
import openfl.Assets;
import openfl.utils.AssetType;
import promhx.Promise;
import ru.m.puzzlez.core.Id.ImageId;
import ru.m.puzzlez.render.Background;
import ru.m.puzzlez.storage.ImageStorage;
@:singleton @:template class BackgroundPopup extends PopupView<Background> {
private static var colorsList:Array<Color> = [
'#FFFFFF',
'#001f3f',
'#0074D9',
'#7FDBFF',
'#39CCCC',
'#3D9970',
'#2ECC40',
'#01FF70',
'#FFDC00',
'#FF851B',
'#FF4136',
'#85144b',
'#F012BE',
'#B10DC9',
'#111111',
'#AAAAAA',
'#DDDDDD',
];
@:view("selected") var selectedView:SpriteView;
@:view("colors") var colorsView:DataView<Color, ButtonView>;
@:view("textures") var texturesView:DataView<ImageId, ButtonView>;
public var selected(default, set):Background;
@:provide static var imageStorage:ImageStorage;
public function new() {
super();
colorsView.data = colorsList;
var textures = [];
for (name in Assets.list(AssetType.IMAGE)) {
if (StringTools.startsWith(name, 'resources/texture')) {
textures.push(new ImageId('asset', name));
}
}
texturesView.data = textures;
}
private function set_selected(value:Background):Background {
selected = value;
switch selected {
case NONE:
selectedView.skin = null;
case COLOR(color):
selectedView.skin = Skin.color(color);
case IMAGE(id):
imageStorage.resolve(id).then(result -> {
selectedView.skin = Skin.bitmap(result, REPEAT);
selectedView.toRedraw();
});
}
selectedView.toRedraw();
return selected;
}
private function colorButtonFactory(index:Int, color:Color):ButtonView {
var result = new ButtonView();
result.text = " ";
result.skin = Skin.buttonColor(color);
return result;
}
private function textureButtonFactory(index:Int, imageId:ImageId):ButtonView {
var result = new ButtonView();
result.text = " ";
result.skin = Skin.buttonBitmap(Assets.getBitmapData(imageId.id), REPEAT);
return result;
}
public function choise(current:Background):Promise<Background> {
selected = current;
return show();
}
}

View File

@@ -0,0 +1,49 @@
---
view:
$type: hw.view.group.VGroupView
geometry.width: 660
geometry.height: 200
geometry.padding: 10
geometry.hAlign: center
geometry.vAlign: middle
layout.margin: 10
style: frame
views:
- $type: hw.view.group.HGroupView
geometry.width: 100%
layout.margin: 10
views:
- id: header
text: Choise background
$type: hw.view.form.LabelView
- id: selected
$type: hw.view.SpriteView
geometry.width: 100%
geometry.height: 200
- id: colors
$type: hw.view.data.DataView
factory: ~colorButtonFactory
geometry.width: 100%
layout:
$type: hw.view.layout.TailLayout
margin: 5
+onDataSelect: ~(color) -> selected = COLOR(color)
- id: textures
$type: hw.view.data.DataView
factory: ~textureButtonFactory
geometry.width: 100%
layout:
$type: hw.view.layout.TailLayout
margin: 5
+onDataSelect: ~(imageId) -> selected = IMAGE(imageId)
- $type: hw.view.group.HGroupView
geometry.width: 100%
layout.hAlign: center
layout.margin: 10
views:
- $type: hw.view.form.ButtonView
text: Default
+onPress: ~close(NONE)
- $type: hw.view.form.ButtonView
text: OK
+onPress: ~close(selected)

View File

@@ -0,0 +1,22 @@
package ru.m.puzzlez.view.popup;
import flash.events.MouseEvent;
import hw.view.popup.PopupView;
import promhx.Promise;
import ru.m.puzzlez.core.GameState;
import ru.m.puzzlez.view.common.PresetView;
@:singleton @:template class PreviewPopup extends PopupView<Dynamic> {
@:view("preview") var previewView:PresetView;
public function new() {
super();
content.addEventListener(MouseEvent.CLICK, _ -> close(null));
}
public function showPreview(state:GameState):Promise<Dynamic> {
previewView.state = state;
return show();
}
}

View File

@@ -0,0 +1,29 @@
---
view:
$type: hw.view.group.VGroupView
geometry.width: 100%
geometry.height: 100%
geometry.padding: 10
geometry.hAlign: center
geometry.vAlign: middle
layout.margin: 10
style: frame
views:
- $type: hw.view.group.HGroupView
geometry.width: 100%
layout.margin: 10
views:
- id: header
text: Image preview
$type: hw.view.form.LabelView
- id: preview
$type: ru.m.puzzlez.view.common.PresetView
geometry.stretch: true
- $type: hw.view.group.HGroupView
geometry.width: 100%
layout.hAlign: center
layout.margin: 10
views:
- $type: hw.view.form.ButtonView
text: OK
+onPress: ~close(null)