diff --git a/package.json b/package.json index e471bf7..c2712da 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "puzzlez", - "version": "0.2.1", + "version": "0.2.2", "private": true, "devDependencies": { "dateformat": "^3.0.3", @@ -15,7 +15,8 @@ "haxework": "git@bitbucket.org:shmyga/haxework.git", "lime": "7.6.3", "openfl": "8.9.5", - "hxcpp": "4.0.52" + "hxcpp": "4.0.52", + "svg": "1.1.3" }, "dependencies": {} } diff --git a/src/haxe/ru/m/puzzlez/PuzzlezTheme.hx b/src/haxe/ru/m/puzzlez/PuzzlezTheme.hx index 5726286..bac60d9 100644 --- a/src/haxe/ru/m/puzzlez/PuzzlezTheme.hx +++ b/src/haxe/ru/m/puzzlez/PuzzlezTheme.hx @@ -4,11 +4,13 @@ import haxework.color.Color; import haxework.view.geometry.Box; import haxework.view.geometry.SizeValue; import haxework.view.theme.Theme; +import openfl.Assets; +import ru.m.skin.ButtonSVGSkin; class PuzzlezTheme extends Theme { public function new() { - super({name: "Georgia"}, {light: "gray"}, {base: 22}); + super({embed: true}, {light: "gray"}, {base: 22}); data.get("frame").data.set("geometry.padding", Box.fromFloat(8)); register(new Style("view", [ "skin.background.color" => colors.light, @@ -20,5 +22,24 @@ class PuzzlezTheme extends Theme { register(new Style("text.error", [ "font.color" => Color.fromString("red"), ], "text")); + registerButton("close", "times-circle-solid.svg", false, 0xcc5500); + } + + private function registerButton(name:String, resource:String, solid:Bool = false, color:Color = null):Void { + if (color == null) { + color = colors.light; + } + var size = 42; + var smallSize = 32; + register(new Style('button.$name', [ + "geometry.width" => SizeValue.fromInt(size), + "geometry.height" => SizeValue.fromInt(size), + "skin" => function() return new ButtonSVGSkin(Assets.getText('resources/image/icon/$resource'), color, solid), + ])); + register(new Style('button.$name.small', [ + "geometry.width" => SizeValue.fromInt(smallSize), + "geometry.height" => SizeValue.fromInt(smallSize), + "skin" => function() return new ButtonSVGSkin(Assets.getText('resources/image/icon/$resource'), color, solid), + ])); } } diff --git a/src/haxe/ru/m/puzzlez/source/FileSource.hx b/src/haxe/ru/m/puzzlez/source/FileSource.hx index 839929c..b8a8c39 100644 --- a/src/haxe/ru/m/puzzlez/source/FileSource.hx +++ b/src/haxe/ru/m/puzzlez/source/FileSource.hx @@ -3,6 +3,7 @@ package ru.m.puzzlez.source; import flash.display.BitmapData; import flash.net.SharedObject; import haxe.crypto.Md5; +import haxe.DynamicAccess; import haxe.io.Bytes; import promhx.Promise; import ru.m.puzzlez.core.Id; @@ -50,7 +51,8 @@ class FileSource implements IImageSource { var fileData = SharedObject.getLocal(id); fileData.clear(); fileData.flush(); - Reflect.deleteField(listData.data, id); + var access:DynamicAccess = listData.data; + access.remove(id); listData.flush(); } } diff --git a/src/haxe/ru/m/puzzlez/source/GameStorage.hx b/src/haxe/ru/m/puzzlez/storage/GameStorage.hx similarity index 95% rename from src/haxe/ru/m/puzzlez/source/GameStorage.hx rename to src/haxe/ru/m/puzzlez/storage/GameStorage.hx index 1f644d7..5ff214d 100644 --- a/src/haxe/ru/m/puzzlez/source/GameStorage.hx +++ b/src/haxe/ru/m/puzzlez/storage/GameStorage.hx @@ -1,4 +1,4 @@ -package ru.m.puzzlez.source; +package ru.m.puzzlez.storage; import haxework.storage.SharedObjectStorage; import ru.m.puzzlez.core.GameState; diff --git a/src/haxe/ru/m/puzzlez/view/GameFrame.hx b/src/haxe/ru/m/puzzlez/view/GameFrame.hx index 81f571d..1e5d912 100644 --- a/src/haxe/ru/m/puzzlez/view/GameFrame.hx +++ b/src/haxe/ru/m/puzzlez/view/GameFrame.hx @@ -7,7 +7,7 @@ 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.source.GameStorage; +import ru.m.puzzlez.storage.GameStorage; @:template class GameFrame extends FrameView { public static var ID = "game"; diff --git a/src/haxe/ru/m/puzzlez/view/GameListFrame.hx b/src/haxe/ru/m/puzzlez/view/GameListFrame.hx index ef4a0ac..f18d52e 100644 --- a/src/haxe/ru/m/puzzlez/view/GameListFrame.hx +++ b/src/haxe/ru/m/puzzlez/view/GameListFrame.hx @@ -3,15 +3,16 @@ package ru.m.puzzlez.view; import haxework.view.data.DataView; import haxework.view.frame.FrameSwitcher; import haxework.view.frame.FrameView; -import ru.m.puzzlez.core.GameState; -import ru.m.puzzlez.source.GameStorage; +import haxework.view.popup.ConfirmView; +import ru.m.puzzlez.core.Id.ImageId; +import ru.m.puzzlez.storage.GameStorage; import ru.m.puzzlez.view.PuzzleImageView; @:template class GameListFrame extends FrameView { public static var ID(default, never) = "game_list"; - @:view var images:ActionDataView; + @:view var images:ActionDataView; @:provide var switcher:FrameSwitcher; @:provide var storage:GameStorage; @@ -21,18 +22,22 @@ import ru.m.puzzlez.view.PuzzleImageView; } override public function onShow(data:Dynamic):Void { - images.data = storage.list(); + images.data = storage.listIds(); } - private function start(state:GameState):Void { - switcher.change(GameFrame.ID, state); + private function start(id:ImageId):Void { + switcher.change(GameFrame.ID, storage.read(id)); } - private function onAction(state:GameState, action:Action):Void { + private function onAction(id:ImageId, action:Action):Void { switch action { - case CLOSE: - storage.delete(state.preset.imageId); - images.data = storage.list(); + case REMOVE: + ConfirmView.confirm("Delete?").then(function(result) { + if (result) { + storage.delete(id); + images.data = storage.listIds(); + } + }); } } diff --git a/src/haxe/ru/m/puzzlez/view/GameListFrame.yaml b/src/haxe/ru/m/puzzlez/view/GameListFrame.yaml index ee6b516..e559f78 100644 --- a/src/haxe/ru/m/puzzlez/view/GameListFrame.yaml +++ b/src/haxe/ru/m/puzzlez/view/GameListFrame.yaml @@ -8,7 +8,7 @@ views: margin: 5 vAlign: middle geometry.stretch: true - factory: ~ru.m.puzzlez.view.PuzzleImageView.stateFactory + factory: ~ru.m.puzzlez.view.PuzzleImageView.factory +onDataSelect: ~start +onDataAction: ~onAction geometry.margin: 5 diff --git a/src/haxe/ru/m/puzzlez/view/ImageListFrame.hx b/src/haxe/ru/m/puzzlez/view/ImageListFrame.hx index 751b462..b064c5d 100644 --- a/src/haxe/ru/m/puzzlez/view/ImageListFrame.hx +++ b/src/haxe/ru/m/puzzlez/view/ImageListFrame.hx @@ -4,17 +4,21 @@ import haxework.view.data.DataView; import haxework.view.form.ButtonView; import haxework.view.frame.FrameSwitcher; import haxework.view.frame.FrameView; +import haxework.view.popup.ConfirmView; import ru.m.puzzlez.core.Id; 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.PuzzleImageView; @:template class ImageListFrame extends FrameView> { public static var ID = "image_list"; - @:view var images:DataView; + @:view var images:ActionDataView; @:view var select:ButtonView; @:provide var imageStorage:ImageStorage; + @:provide var gameStorage:GameStorage; @:provide var switcher:FrameSwitcher; private var source:ImageListSource; @@ -47,8 +51,28 @@ import ru.m.puzzlez.storage.ImageStorage; }); } - private function start(image:ImageId):Void { - switcher.change(PresetFrame.ID, image); + private function onAction(imageId:ImageId, action:Action):Void { + switch action { + case REMOVE: + var fileSource:FileSource = Std.instance(source.source, FileSource); + if (fileSource != null) { + ConfirmView.confirm("Delete?").then(function(result) { + if (result) { + fileSource.remove(imageId); + loading.promise = fileSource.getList().then(function(result) images.data = result); + } + }); + } + } + } + + private function start(imageId:ImageId):Void { + var state = gameStorage.read(imageId); + if (state != null) { + switcher.change(GameFrame.ID, state); + } else { + switcher.change(PresetFrame.ID, imageId); + } } private function back():Void { diff --git a/src/haxe/ru/m/puzzlez/view/ImageListFrame.yaml b/src/haxe/ru/m/puzzlez/view/ImageListFrame.yaml index 689db5c..3c86891 100644 --- a/src/haxe/ru/m/puzzlez/view/ImageListFrame.yaml +++ b/src/haxe/ru/m/puzzlez/view/ImageListFrame.yaml @@ -2,7 +2,7 @@ style: frame views: - id: images - $type: haxework.view.data.DataView + $type: haxework.view.data.ActionDataView layout: $type: haxework.view.layout.TailLayout margin: 5 @@ -10,6 +10,7 @@ views: geometry.stretch: true factory: ~ru.m.puzzlez.view.PuzzleImageView.factory +onDataSelect: ~start + +onDataAction: ~onAction geometry.margin: 5 overflow.y: scroll - id: select diff --git a/src/haxe/ru/m/puzzlez/view/PuzzleImageView.hx b/src/haxe/ru/m/puzzlez/view/PuzzleImageView.hx index bfbe28f..33b9f7c 100644 --- a/src/haxe/ru/m/puzzlez/view/PuzzleImageView.hx +++ b/src/haxe/ru/m/puzzlez/view/PuzzleImageView.hx @@ -1,16 +1,17 @@ package ru.m.puzzlez.view; -import haxework.view.data.DataView.ActionDataView; +import haxework.view.data.DataView; +import haxework.view.form.ButtonView; import haxework.view.form.LabelView; import haxework.view.group.GroupView; import haxework.view.ImageView; -import ru.m.puzzlez.core.GameState; import ru.m.puzzlez.core.GameUtil; import ru.m.puzzlez.core.Id; +import ru.m.puzzlez.storage.GameStorage; import ru.m.puzzlez.storage.ImageStorage; enum Action { - CLOSE; + REMOVE; } @:template class PuzzleImageView extends GroupView { @@ -37,7 +38,9 @@ enum Action { @:view("image") var imageView:ImageView; @:view("label") var labelView:LabelView; - @:provide var imageStorage:ImageStorage; + @:view("remove") var removeButton:ButtonView; + @:provide static var imageStorage:ImageStorage; + @:provide static var gameStorage:GameStorage; private var loading:LoadingWrapper; public function new() { @@ -45,25 +48,22 @@ enum Action { loading = new LoadingWrapper(this); } - private function close():Void { + private function emit(action:Action):Void { var dataView:ActionDataView = Std.instance(parent, ActionDataView); if (dataView != null) { var index = dataView.dataViews.indexOf(this); - dataView.onDataAction.emit(dataView.data[index], CLOSE); + dataView.onDataAction.emit(dataView.data[index], action); } } public static function factory(index:Int, imageId:ImageId):PuzzleImageView { var result = new PuzzleImageView(); result.imageId = imageId; - return result; - } - - public static function stateFactory(index:Int, state:GameState):PuzzleImageView { - var result = new PuzzleImageView(); - result.imageId = state.preset.imageId; - var progress = GameUtil.calcProgress(state); - result.text = '${progress.complete}/${progress.total}'; + var state = gameStorage.read(imageId); + if (state != null) { + var progress = GameUtil.calcProgress(state); + result.text = '${progress.complete}/${progress.total}'; + } return result; } } diff --git a/src/haxe/ru/m/puzzlez/view/PuzzleImageView.yaml b/src/haxe/ru/m/puzzlez/view/PuzzleImageView.yaml index cf2ee1e..1df16e3 100644 --- a/src/haxe/ru/m/puzzlez/view/PuzzleImageView.yaml +++ b/src/haxe/ru/m/puzzlez/view/PuzzleImageView.yaml @@ -8,9 +8,9 @@ views: fillType: COVER - id: label $type: haxework.view.form.LabelView - - id: close + - id: remove $type: haxework.view.form.ButtonView propagation: false geometry.hAlign: right - text: X - +onPress: ~close() + style: button.close.small + +onPress: ~emit(Action.REMOVE) diff --git a/src/haxe/ru/m/skin/ButtonSVGSkin.hx b/src/haxe/ru/m/skin/ButtonSVGSkin.hx new file mode 100644 index 0000000..807002e --- /dev/null +++ b/src/haxe/ru/m/skin/ButtonSVGSkin.hx @@ -0,0 +1,56 @@ +package ru.m.skin; + +import format.SVG; +import haxework.color.Color; +import haxework.view.form.ButtonView; +import haxework.view.skin.ISkin; + +using StringTools; +using haxework.color.ColorUtil; + +@:style class ButtonSVGSkin implements ISkin { + + @:style(null) public var svg:String; + @:style(0) public var color:Null; + @:style(false) public var solid:Null; + + private var svgs:Map; + + public function new(?svg:String, ?color:Color, ?solid:Bool) { + this.svg = svg; + this.color = color; + this.solid = solid; + init(solid); + } + + private inline function buildSVG(color:Color):SVG { + return new SVG(svg.replace("currentColor", '#${color}')); + } + + private function init(solid:Bool):Void { + var color = color; + if (solid) { + color = color.multiply(1.5); + } + svgs = new Map(); + svgs.set(UP, buildSVG(color)); + svgs.set(DOWN, buildSVG(color.diff(-24))); + svgs.set(OVER, buildSVG(color.diff(24))); + svgs.set(DISABLED, buildSVG(color.grey())); + } + + public function draw(view:ButtonView):Void { + var svg = svgs.get(view.state); + var graphics = view.content.graphics; + graphics.beginFill(0, 0); + graphics.drawRect(0, 0, view.width, view.height); + graphics.beginFill(color); + if (!solid) { + graphics.lineStyle(2, color.multiply(1.5)); + } + // ToDo: padding + svg.render(graphics, 0, 0, Std.int(view.width * 0.8), Std.int(view.height * 0.8)); + graphics.lineStyle(); + graphics.endFill(); + } +} diff --git a/src/resources/fonts/GamePlayed.eot b/src/resources/fonts/GamePlayed.eot new file mode 100644 index 0000000..79b7a73 Binary files /dev/null and b/src/resources/fonts/GamePlayed.eot differ diff --git a/src/resources/fonts/GamePlayed.svg b/src/resources/fonts/GamePlayed.svg new file mode 100644 index 0000000..6008db2 Binary files /dev/null and b/src/resources/fonts/GamePlayed.svg differ diff --git a/src/resources/fonts/GamePlayed.ttf b/src/resources/fonts/GamePlayed.ttf new file mode 100644 index 0000000..f44116f Binary files /dev/null and b/src/resources/fonts/GamePlayed.ttf differ diff --git a/src/resources/fonts/GamePlayed.woff b/src/resources/fonts/GamePlayed.woff new file mode 100644 index 0000000..676be4a Binary files /dev/null and b/src/resources/fonts/GamePlayed.woff differ diff --git a/src/resources/image/icon/arrow-alt-circle-left-solid.svg b/src/resources/image/icon/arrow-alt-circle-left-solid.svg new file mode 100644 index 0000000..32c31ee --- /dev/null +++ b/src/resources/image/icon/arrow-alt-circle-left-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/resources/image/icon/arrow-alt-circle-right-solid.svg b/src/resources/image/icon/arrow-alt-circle-right-solid.svg new file mode 100644 index 0000000..523c283 --- /dev/null +++ b/src/resources/image/icon/arrow-alt-circle-right-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/resources/image/icon/backspace-light.svg b/src/resources/image/icon/backspace-light.svg new file mode 100644 index 0000000..467fb78 --- /dev/null +++ b/src/resources/image/icon/backspace-light.svg @@ -0,0 +1,6 @@ + diff --git a/src/resources/image/icon/bars-solid.svg b/src/resources/image/icon/bars-solid.svg new file mode 100644 index 0000000..8a8b715 --- /dev/null +++ b/src/resources/image/icon/bars-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/resources/image/icon/cog-solid.svg b/src/resources/image/icon/cog-solid.svg new file mode 100644 index 0000000..0bc8d22 --- /dev/null +++ b/src/resources/image/icon/cog-solid.svg @@ -0,0 +1 @@ + diff --git a/src/resources/image/icon/keyboard-light.svg b/src/resources/image/icon/keyboard-light.svg new file mode 100644 index 0000000..8dc7ee9 --- /dev/null +++ b/src/resources/image/icon/keyboard-light.svg @@ -0,0 +1,6 @@ + diff --git a/src/resources/image/icon/play-circle-solid.svg b/src/resources/image/icon/play-circle-solid.svg new file mode 100644 index 0000000..cc067c5 --- /dev/null +++ b/src/resources/image/icon/play-circle-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/resources/image/icon/sign-in-solid.svg b/src/resources/image/icon/sign-in-solid.svg new file mode 100644 index 0000000..a6b7b3d --- /dev/null +++ b/src/resources/image/icon/sign-in-solid.svg @@ -0,0 +1 @@ + diff --git a/src/resources/image/icon/sign-out-solid.svg b/src/resources/image/icon/sign-out-solid.svg new file mode 100644 index 0000000..efbfbbb --- /dev/null +++ b/src/resources/image/icon/sign-out-solid.svg @@ -0,0 +1 @@ + diff --git a/src/resources/image/icon/tablet-android-alt-light.svg b/src/resources/image/icon/tablet-android-alt-light.svg new file mode 100644 index 0000000..35ef4c5 --- /dev/null +++ b/src/resources/image/icon/tablet-android-alt-light.svg @@ -0,0 +1,7 @@ + diff --git a/src/resources/image/icon/times-circle-solid.svg b/src/resources/image/icon/times-circle-solid.svg new file mode 100644 index 0000000..9046b0f --- /dev/null +++ b/src/resources/image/icon/times-circle-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/resources/image/icon/window-close-solid.svg b/src/resources/image/icon/window-close-solid.svg new file mode 100644 index 0000000..eaa6802 --- /dev/null +++ b/src/resources/image/icon/window-close-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file