Merge lp:~ken-vandine/dropping-letters/toolbar into lp:dropping-letters

Proposed by Ken VanDine
Status: Merged
Approved by: Alan Pope 🍺🐧🐱 πŸ¦„
Approved revision: 29
Merged at revision: 24
Proposed branch: lp:~ken-vandine/dropping-letters/toolbar
Merge into: lp:dropping-letters
Diff against target: 1291 lines (+608/-602)
5 files modified
debian/changelog (+11/-1)
debian/control (+2/-2)
debian/install (+2/-1)
dropping-letters.qml (+592/-597)
dropping-letters.qmlproject (+1/-1)
To merge this branch: bzr merge lp:~ken-vandine/dropping-letters/toolbar
Reviewer Review Type Date Requested Status
Alan Pope 🍺🐧🐱 πŸ¦„ (community) Approve
Review via email: mp+162706@code.launchpad.net

Commit message

* Use a toolbar for volume and new game instead of the small items on the
  bottombar
* Add HUD support

Description of the change

* Use a toolbar for volume and new game instead of the small items on the
  bottombar
* Add HUD support

To post a comment you must log in.
28. By Ken VanDine

Use a toolbar for volume and new game instead of the small items on the
bottombar

29. By Ken VanDine

added New game to HUD

Revision history for this message
Alan Pope 🍺🐧🐱 πŸ¦„ (popey) wrote :

Looks good.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/changelog'
2--- debian/changelog 2013-05-01 05:01:59 +0000
3+++ debian/changelog 2013-05-07 05:11:28 +0000
4@@ -1,4 +1,14 @@
5-dropping-letters (0.1) UNRELEASED; urgency=low
6+dropping-letters (0.1.1) UNRELEASED; urgency=low
7+
8+ * Use a toolbar for volume and new game instead of the small items on the
9+ bottombar
10+ * Add HUD support
11+ * debian/control
12+ - depend on qtdeclarative5-hud1.0
13+
14+ -- Ken VanDine <ken.vandine@canonical.com> Wed, 01 May 2013 18:00:42 -0700
15+
16+dropping-letters (0.1) raring; urgency=low
17
18 * Initial Release.
19
20
21=== modified file 'debian/control'
22--- debian/control 2013-05-01 05:01:59 +0000
23+++ debian/control 2013-05-07 05:11:28 +0000
24@@ -5,11 +5,11 @@
25 Build-Depends: debhelper (>= 9.0.0)
26 Standards-Version: 3.9.4
27 Homepage: http://launchpad.net/dropping-letters
28-Vcs-Bzr: https://code.launchpad.net/~sil/dropping-letters
29+Vcs-Bzr: https://code.launchpad.net/~dropping-letters-devs/dropping-letters/trunk
30
31 Package: dropping-letters
32 Architecture: all
33-Depends: ${misc:Depends},qtdeclarative5-localstorage-plugin,qtdeclarative5-particles-plugin,qtdeclarative5-qtmultimedia-plugin,qtdeclarative5-qtquick2-plugin,qtdeclarative5-ubuntu-ui-toolkit-plugin|qt-components-ubuntu,qtdeclarative5-window-plugin,qtdeclarative5-xmllistmodel-plugin,qmlscene
34+Depends: ${misc:Depends},qtdeclarative5-hud1.0,qtdeclarative5-localstorage-plugin,qtdeclarative5-particles-plugin,qtdeclarative5-qtmultimedia-plugin,qtdeclarative5-qtquick2-plugin,qtdeclarative5-ubuntu-ui-toolkit-plugin|qt-components-ubuntu,qtdeclarative5-window-plugin,qtdeclarative5-xmllistmodel-plugin,qmlscene
35 Description: Dropping Letters game
36 Build up words from falling letters to score
37 points. Don't let the letters build up to the
38
39=== modified file 'debian/install'
40--- debian/install 2013-05-01 05:01:59 +0000
41+++ debian/install 2013-05-07 05:11:28 +0000
42@@ -1,5 +1,6 @@
43-dropping-letters-ubuntu.qml /usr/share/dropping-letters
44+dropping-letters.qml /usr/share/dropping-letters
45 volume-sprite.png /usr/share/dropping-letters
46+new.png /usr/share/dropping-letters
47 test_load_binarydict.js /usr/share/dropping-letters
48 80921__justinbw__buttonchime02up.ogg /usr/share/dropping-letters
49 audio-volume-high.svg /usr/share/dropping-letters
50
51=== renamed file 'dropping-letters-ubuntu.qml' => 'dropping-letters.qml'
52--- dropping-letters-ubuntu.qml 2013-05-01 06:10:15 +0000
53+++ dropping-letters.qml 2013-05-07 05:11:28 +0000
54@@ -5,6 +5,7 @@
55 import QtQuick.LocalStorage 2.0
56 import Ubuntu.Components 0.1
57 import Ubuntu.Components.Popups 0.1
58+import Ubuntu.HUD 1.0 as HUD
59
60 MainView {
61 id: mainView
62@@ -12,617 +13,598 @@
63 width: units.gu(45) // 48 * 7 + 2 * 7 + 2
64 height: units.gu(65) // 48 * 10 + 2 * 10 + 50
65
66- Flipable {
67- id: flipable
68- property bool flipped: false
69- property variant db: null
70- anchors.fill: parent
71-
72- transform: Rotation {
73- id: rotation
74- origin.x: flipable.width/2
75- origin.y: flipable.height/2
76- axis.x: 0; axis.y: 1; axis.z: 0 // set axis.y to 1 to rotate around y-axis
77- angle: 0 // the default angle
78- }
79- property int minChromeHeight: 50
80-
81- Component.onCompleted: {
82- var db = LocalStorage.openDatabaseSync("dropping-letters", "1.0", "Dropping Letters", 1000);
83- db.transaction(function(tx) {
84- // Create the database if it doesn't already exist
85- tx.executeSql('CREATE TABLE IF NOT EXISTS Scores(score INT, time TEXT)');
86- var res = tx.executeSql('SELECT score from Scores order by score DESC LIMIT 1');
87- if (res.rows.length === 0) {
88- bestscore.updateScore(0);
89- } else {
90- bestscore.updateScore(res.rows.item(0).score);
91- }
92- });
93- }
94-
95- states: [
96- State {
97- name: "back"
98- PropertyChanges { target: rotation; angle: 180 }
99- when: flipable.flipped
100- },
101- State {
102- name: "front"
103- when: !flipable.flipped
104- }
105- ]
106-
107- transitions: [
108- Transition {
109- NumberAnimation { target: rotation; property: "angle"; duration: 200 }
110- }
111- ]
112-
113- front: Rectangle {
114- id: entry
115- color: "#efefea"
116- width: flipable.width // 48 * 7 + 2 * 7 + 2
117- height: flipable.height // 48 * 10 + 2 * 10 + 50
118-
119- Column {
120- id: logo
121- anchors.centerIn: parent
122- //spacing: units.dp(2) // 2
123- Repeater {
124- id: titleRowRepeater
125- model: ["DROP", "PING", "LETT", "ERS "]
126- Row {
127- id: titleRow
128- //spacing: units.dp(2) // 2
129- property int lineIndex: index
130- property string modelString: modelData
131- Repeater {
132- id: titleColRepeater
133- model: modelData.split("")
134- Rectangle {
135- id: titleletter
136- width: Math.max(flipable.width * 0.4, flipable.height * 0.4) / titleRowRepeater.count - titleRowRepeater.count * titleRow.spacing
137- height: Math.max(flipable.width * 0.4, flipable.height * 0.4) / titleRowRepeater.count - titleRowRepeater.count * titleRow.spacing
138- color: Qt.hsla(210/360, 0.84, 0.25 + ((4-parent.lineIndex) * 0.05) + ((4-index) * 0.05));
139- //radius: "medium"
140- Label {
141- text: modelData
142- anchors.centerIn: parent
143- fontSize: "x-large"
144- font.bold: true
145- color: "#ffffff"
146- }
147- states: [
148- State { name: "showing"; when: flipable.state == "front"
149- PropertyChanges { target: titleletter; y: 0 }
150- },
151- State { name: "notshowing"; when: flipable.state != "front"
152- PropertyChanges { target: titleletter; y: -units.gu(300) }
153- }
154- ]
155- Behavior on y {
156- SequentialAnimation {
157- PauseAnimation {
158- duration: (35 * parent.modelString.length * (4-parent.lineIndex)) + (
159- 35 * index) }
160- NumberAnimation { easing.type: Easing.OutQuart }
161- }
162- }
163- }
164- }
165- }
166- }
167- }
168- Label {
169- id: playbutton
170- text: "play"
171- anchors.top: logo.bottom
172- anchors.topMargin: units.gu(2)
173- anchors.horizontalCenter: logo.horizontalCenter
174- fontSize: "x-large"
175- color: "#222222"
176- }
177- Label {
178- id: bestscore
179- property int bestsofar: 0
180- text: "..."
181- anchors.top: playbutton.bottom
182- anchors.topMargin: units.gu(2)
183- anchors.horizontalCenter: logo.horizontalCenter
184- fontSize: "large"
185- color: "#222222"
186- function updateScore(score) {
187- if (score >= bestscore.bestsofar) {
188- bestscore.text = "Best score: " + score;
189- bestscore.bestsofar = score;
190- }
191- }
192- }
193- MouseArea {
194- anchors.fill: parent
195- enabled: !flipable.flipped
196- onClicked: {
197- lm.clear();
198- for (var i=0; i<7; i++) {
199- lm.append({'letters': [] });
200- }
201- accum.text = "";
202- droptimer.tickCount = 0;
203- flipable.flipped = true
204- }
205- }
206-
207- Rectangle {
208- id: helpbutton
209- width: units.gu(5)
210- height: units.gu(5)
211- radius: units.gu(1)
212- Label {
213- text: "?"
214- anchors.centerIn: parent
215- }
216- anchors.top: parent.top
217- anchors.right: parent.right
218- anchors.margins: units.gu(2)
219- MouseArea {
220- anchors.fill: parent
221- onClicked: {
222- PopupUtils.open(helppop, helpbutton)
223- }
224- }
225- }
226- Component {
227- id: helppop
228- Popover {
229- id: helpsheet
230- Rectangle {
231- anchors.top: parent.top
232- anchors.left: parent.left
233- anchors.right: parent.right
234- height: units.gu(15)
235- border.color: "#ddd"
236- radius: 2
237- Label {
238- anchors.fill: parent
239- anchors.margins: units.gu(1)
240- text: "game by <a href='http://twitter.com/sil'>@sil</a><br>music: <a href='http://incompetech.com/music/royalty-free/index.html?isrc=USUAN1200076'>Easy Lemon</a> from Kevin McCloud (incompetech.com)<br>sounds: freesound from <a href='http://www.freesound.org/people/kantouth/sounds/106727/'>kantouth</a> / <a href='http://www.freesound.org/people/tictacshutup/sounds/407/'>tictacshutup</a> / <a href='http://www.freesound.org/people/dj-chronos/sounds/45137/'>dj-chronos</a> / <a href='http://www.freesound.org/people/justinbw/sounds/80921/'>justinbw</a>"
241- wrapMode: Text.WordWrap
242- textFormat: Text.StyledText
243- onLinkActivated: Qt.openUrlExternally(link)
244- }
245- }
246- }
247- }
248-
249- }
250-
251- back: Rectangle {
252- id: main
253- color: "#58585A"
254- width: flipable.width // 48 * 7 + 2 * 7 + 2
255- height: flipable.height // 48 * 10 + 2 * 10 + 50
256- property var selectedItems: []
257- /*
258- obtained with:
259- import re; fp=open('/usr/share/dict/words');
260- shorts=[re.sub('[^a-zA-Z]','',x.lower().strip()) for x in fp if len(x)<7];
261- from collections import Counter; c=Counter(''.join(shorts)); least=c.most_common()[-1][1];
262- print dict([(x.upper(),c[x]/least) for x in c])
263- */
264- property variant letterFreqs: {
265- 'A': 66, 'C': 22, 'B': 19, 'E': 72, 'D': 28, 'G': 18, 'F': 12, 'I': 41, 'H': 20,
266- 'K': 14, 'J': 4, 'M': 22, 'L': 40, 'O': 48, 'N': 35, 'Q': 1, 'P': 22, 'S': 75,
267- 'R': 43, 'U': 26, 'T': 37, 'W': 13, 'V': 7, 'Y': 19, 'X': 3, 'Z': 4
268- }
269- property variant letterScores: {
270- "A":1,"B":3,"C":3,"D":2,"E":1,"F":4,"G":2,"H":4,"I":1,"J":8,"K":5,"L":1,
271- "M":3,"N":1,"O":1,"P":3,"Q":10,"R":1,"S":1,"T":1,"U":1,"V":4,"W":4,"X":8,
272- "Y":4,"Z":10
273- }
274- property int score: 0
275-
276- function getRandomWeightedLetter() {
277- // could work out sumOfWeights once, but this is easier to understand
278- var sumOfWeights = 0;
279- for (var k in main.letterFreqs) { sumOfWeights += main.letterFreqs[k]; }
280- var selection = Math.floor(Math.random() * sumOfWeights);
281- for (var k in main.letterFreqs) {
282- if (selection < main.letterFreqs[k]) return k;
283- selection -= main.letterFreqs[k];
284- }
285- }
286-
287- Audio {
288- id: music
289- source: "Easy_Lemon_60_second.ogg"
290- autoPlay: true
291- onStopped: music.play()
292- muted: !volume.audible
293- }
294-
295- Audio {
296- id: click
297- source: "407__tictacshutup__click-1-off-click.ogg"
298- autoLoad: true
299- muted: !volume.audible
300- }
301-
302- Audio {
303- id: success
304- source: "80921__justinbw__buttonchime02up.ogg"
305- autoLoad: true
306- muted: !volume.audible
307- }
308-
309- Audio {
310- id: failure
311- source: "106727__kantouth__cartoon-bing-low.ogg"
312- autoLoad: true
313- muted: !volume.audible
314- }
315-
316- Audio {
317- id: gameover
318- source: "45137__dj-chronos__dark-church-bell.ogg"
319- autoLoad: true
320- onStopped: {
321- flipable.flipped = false
322- }
323- muted: !volume.audible
324- }
325-
326- Rectangle {
327- id: bottombar
328- anchors.bottom: main.bottom
329- anchors.left: main.left
330- anchors.right: main.right
331- height: (main.height - (game.squaresize * 10 + 11)) / 2
332- z: 2
333- color: Qt.hsla(210/360, 0.84, 0.2)
334-
335- Rectangle {
336- id: mainscore
337- anchors.right: quitgame.left
338- anchors.top: bottombar.top
339- anchors.bottom: bottombar.bottom
340- anchors.left: volume.right
341- z: 2
342- color: Qt.rgba(0,0,0,0)
343- Label {
344- anchors.centerIn: parent
345- fontSize: "x-large"
346- text: "" + main.score
347- color: "white"
348- }
349- }
350-
351- Rectangle {
352- id: quitgame
353- width: bottombar.height
354- height: bottombar.height
355- color: Qt.rgba(0,0,0,0)
356- anchors.right: bottombar.right
357- anchors.top: bottombar.top
358- z: 3
359-
360- Label {
361- anchors.centerIn: parent
362- text: "\u00d7"
363- fontSize: "x-large"
364- color: "white"
365- MouseArea {
366- anchors.fill: parent
367- onClicked: {
368- flipable.flipped = false;
369- }
370- }
371- }
372- }
373-
374- Rectangle {
375- id: volume
376- width: bottombar.height
377- height: bottombar.height + 1
378- color: Qt.rgba(0,0,0,0)
379- anchors.left: bottombar.left
380- anchors.bottom: bottombar.bottom
381- z: 3
382- clip: true
383- property int frame: 0
384- property bool reverse: false
385- property bool audible: true
386- Image {
387- id: image
388- sourceSize.width: parent.height * 0.8
389- sourceSize.height: parent.height * 0.8
390- anchors.centerIn: parent
391- source: volume.audible ? "audio-volume-high-symbolic.svg" : "audio-volume-muted-symbolic.svg";
392- }
393- MouseArea {
394- anchors.fill: parent
395- onClicked: {
396- volume.audible = !volume.audible
397- }
398- }
399- }
400- }
401-
402- Rectangle {
403- id: topbar
404- color: "blue"
405- anchors.top: main.top
406- anchors.left: main.left
407- anchors.right: main.right
408- height: (main.height - (game.squaresize * 10 + 11)) / 2
409- z: 2
410-
411- Rectangle {
412- anchors.fill: topbar
413- z: 2
414- color: accum.text == "" ? "#efefea" : (accum.isValid ? Qt.hsla(124/360, 0.83, 0.45) : Qt.hsla(0, 0.83, 0.65))
415- id: suggestedWordContainer
416- Label {
417- id: accum
418- anchors.centerIn: parent
419- text: ""
420- color: "white"
421- fontSize: "x-large"
422- font.bold: true
423- property bool isValid: false
424- function findBinaryWord( word ) {
425- var bd = Wordlist.wordlist;
426- var l = word.length;
427- // Don't search if there's nothing to look through
428- if ( !bd[l] ) {
429- return false;
430- }
431- // Get the number of words in the dictionary bin
432- var words = bd[l].length / l,
433- // The low point from where we're starting the binary search
434- low = 0,
435- // The max high point
436- high = words - 1,
437- // And the precise middle of the search
438- mid = Math.floor( words / 2 );
439- // We continue to look until we reach a final word
440- while ( high >= low ) {
441- // Grab the word at our current position
442- var found = bd[l].substr( l * mid, l );
443- // If we've found the word, stop now
444- if ( word === found ) {
445- return true;
446- }
447- // Otherwise, compare
448- // If we're too high, move lower
449- if ( word < found ) {
450- high = mid - 1;
451- // If we're too low, go higher
452- } else {
453- low = mid + 1;
454- }
455- // And find the new search point
456- mid = Math.floor( (low + high) / 2 );
457- }
458- // Nothing was found
459- return false;
460- }
461- onTextChanged: {
462- accum.isValid = findBinaryWord(accum.text);
463- }
464- }
465- function processSuggestedWord() {
466- if (accum.isValid) {
467- success.play();
468- var thisscore = 0, wordlength = accum.text.length;
469- accum.text = "";
470-
471- // tell the boxes to destroy themselves
472- main.selectedItems.forEach(function(b) {
473- thisscore += main.letterScores[b.containedLetter];
474- b.state = "dead";
475- })
476- main.selectedItems = [];
477- main.score += thisscore * wordlength;
478- scoredisplay.text = "" + (thisscore * wordlength);
479- showscoredisplay.start();
480- } else {
481- failure.play();
482- accum.text = "";
483- main.selectedItems.forEach(function(b) { b.selected = false; })
484- main.selectedItems = [];
485- }
486- }
487- focus: true
488- Keys.onReturnPressed: {
489- suggestedWordContainer.processSuggestedWord();
490- }
491- MouseArea {
492- anchors.fill: parent
493- onClicked: { suggestedWordContainer.processSuggestedWord(); }
494- }
495- }
496-
497-
498- }
499-
500- Label {
501- id: scoredisplay
502- anchors.centerIn: parent
503- z: 3
504- fontSize: "x-large"
505- text: "200"
506- color: "red"
507- opacity: 0
508- }
509-
510- ParallelAnimation {
511- id: showscoredisplay
512- NumberAnimation {
513- property: "scale"
514- from: 0.1
515- to: 8.0
516- duration: 400
517- target: scoredisplay
518- }
519- SequentialAnimation {
520- NumberAnimation {
521- property: "opacity"
522- from: 0
523- to: 1.0
524- duration: 20
525- target: scoredisplay
526- }
527- NumberAnimation {
528- property: "opacity"
529- from: 1.0
530- to: 0
531- duration: 380
532- target: scoredisplay
533- }
534- }
535- }
536-
537-
538- Timer {
539- id: droptimer
540- repeat: true
541- running: flipable.flipped
542- interval: 2000
543- property int tickCount: 0
544- triggeredOnStart: true
545- onTriggered: {
546- tickCount += 5
547- droptimer.interval = 2000 - tickCount
548- var idx = Math.round(Math.random() * (lm.count - 1));
549- lm.get(idx).letters.append({ letter: main.getRandomWeightedLetter() });
550- if (lm.get(idx).letters.count >= 10) {
551- droptimer.stop();
552- gameover.play();
553- var db = LocalStorage.openDatabaseSync("dropping-letters", "1.0", "Dropping Letters", 1000);
554- db.transaction(function(tx) {
555- tx.executeSql("insert into Scores values (?,?)", [main.score, 0]);
556- bestscore.updateScore(main.score);
557- });
558- }
559- }
560- }
561-
562- ListModel {
563- id: lm
564- }
565-
566-
567- Rectangle {
568- id: game
569- property int squaresize: Math.min((flipable.width) / 7, (flipable.height - (flipable.minChromeHeight * 2)) / 10)
570- anchors.top: topbar.bottom
571- anchors.bottom: bottombar.top
572- anchors.left: main.left
573- anchors.right: main.right
574- scale: -1
575+
576+ function resetGame () {
577+ lm.clear();
578+ for (var i=0; i<7; i++) {
579+ lm.append({'letters': [] });
580+ }
581+ accum.text = "";
582+ droptimer.tickCount = 0;
583+ flipable.flipped = true;
584+ }
585+
586+ Page {
587+ tools: ToolbarActions {
588+ Action {
589+ text: i18n.tr("Volume")
590+ iconSource: (volume.audible ? "audio-volume-high-symbolic.svg" : "audio-volume-muted-symbolic.svg")
591+ onTriggered: {
592+ volume.audible = !volume.audible;
593+ }
594+ }
595+ Action {
596+ text: i18n.tr("New game")
597+ iconSource: ("new.png")
598+ onTriggered: {
599+ resetGame();
600+ }
601+ }
602+ }
603+
604+ Item {
605+ id: volume
606+ property int frame: 0
607+ property bool reverse: false
608+ property bool audible: true
609+ }
610+
611+ Flipable {
612+ id: flipable
613+ property bool flipped: false
614+ property variant db: null
615+ anchors.fill: parent
616+
617+ transform: Rotation {
618+ id: rotation
619+ origin.x: flipable.width/2
620+ origin.y: flipable.height/2
621+ axis.x: 0; axis.y: 1; axis.z: 0 // set axis.y to 1 to rotate around y-axis
622+ angle: 0 // the default angle
623+ }
624+ property int minChromeHeight: 50
625+
626+ Component.onCompleted: {
627+ var db = LocalStorage.openDatabaseSync("dropping-letters", "1.0", "Dropping Letters", 1000);
628+ db.transaction(function(tx) {
629+ // Create the database if it doesn't already exist
630+ tx.executeSql('CREATE TABLE IF NOT EXISTS Scores(score INT, time TEXT)');
631+ var res = tx.executeSql('SELECT score from Scores order by score DESC LIMIT 1');
632+ if (res.rows.length === 0) {
633+ bestscore.updateScore(0);
634+ } else {
635+ bestscore.updateScore(res.rows.item(0).score);
636+ }
637+ });
638+ }
639+
640+ states: [
641+ State {
642+ name: "back"
643+ PropertyChanges { target: rotation; angle: 180 }
644+ when: flipable.flipped
645+ },
646+ State {
647+ name: "front"
648+ when: !flipable.flipped
649+ }
650+ ]
651+
652+ transitions: [
653+ Transition {
654+ NumberAnimation { target: rotation; property: "angle"; duration: 200 }
655+ }
656+ ]
657+
658+ front: Rectangle {
659+ id: entry
660 color: "#efefea"
661- Row {
662- anchors.horizontalCenter: game.horizontalCenter
663- anchors.top: game.top
664- anchors.topMargin: 1
665- //spacing: 1
666+ width: flipable.width // 48 * 7 + 2 * 7 + 2
667+ height: flipable.height // 48 * 10 + 2 * 10 + 50
668+
669+ Column {
670+ id: logo
671+ anchors.centerIn: parent
672+ //spacing: units.dp(2) // 2
673 Repeater {
674- model: lm
675- Column {
676- //spacing: 1
677- property int idx: index
678- width: game.squaresize
679- height: game.height
680- add: Transition {
681- NumberAnimation { properties: "y"; easing.type: Easing.OutBounce; duration: 1000 }
682- }
683- move: Transition {
684- NumberAnimation { properties: "y"; easing.type: Easing.OutBounce }
685- }
686+ id: titleRowRepeater
687+ model: ["DROP", "PING", "LETT", "ERS "]
688+ Row {
689+ id: titleRow
690+ //spacing: units.dp(2) // 2
691+ property int lineIndex: index
692+ property string modelString: modelData
693 Repeater {
694- model: letters
695+ id: titleColRepeater
696+ model: modelData.split("")
697 Rectangle {
698- id: box
699- property bool selected: false
700- property int idx: index
701- property string containedLetter: letter
702- color: {
703- if (lm.get(parent.idx).letters.count >= 10) {
704- return "red";
705- } else if (!selected) {
706- return Qt.hsla(210/360, 0.84, 0.25 + (idx * 0.05));
707- } else if (accum.isValid) {
708- return Qt.hsla(124/360, 0.83, 0.45); // "#93cc98";
709- } else {
710- return Qt.hsla(0, 0.83, 0.65); // "#cc9598"
711- }
712- }
713+ id: titleletter
714+ width: Math.max(flipable.width * 0.4, flipable.height * 0.4) / titleRowRepeater.count - titleRowRepeater.count * titleRow.spacing
715+ height: Math.max(flipable.width * 0.4, flipable.height * 0.4) / titleRowRepeater.count - titleRowRepeater.count * titleRow.spacing
716+ color: Qt.hsla(210/360, 0.84, 0.25 + ((4-parent.lineIndex) * 0.05) + ((4-index) * 0.05));
717 //radius: "medium"
718- scale: -1
719- width: game.squaresize
720- height: game.squaresize
721- y: game.height + game.squaresize
722- z: 5
723 Label {
724+ text: modelData
725 anchors.centerIn: parent
726- text: letter
727- fontSize: "large"
728+ fontSize: "x-large"
729 font.bold: true
730 color: "#ffffff"
731 }
732- MouseArea {
733- anchors.fill: parent
734- onClicked: {
735- if (!box.selected) {
736- box.selected = true;
737- accum.text += letter;
738- click.play();
739- main.selectedItems[main.selectedItems.length] = box;
740+ states: [
741+ State { name: "showing"; when: flipable.state == "front"
742+ PropertyChanges { target: titleletter; y: 0 }
743+ },
744+ State { name: "notshowing"; when: flipable.state != "front"
745+ PropertyChanges { target: titleletter; y: -units.gu(300) }
746+ }
747+ ]
748+ Behavior on y {
749+ SequentialAnimation {
750+ PauseAnimation {
751+ duration: (35 * parent.modelString.length * (4-parent.lineIndex)) + (
752+ 35 * index) }
753+ NumberAnimation { easing.type: Easing.OutQuart }
754+ }
755+ }
756+ }
757+ }
758+ }
759+ }
760+ }
761+ Label {
762+ id: playbutton
763+ text: i18n.tr("Play")
764+ anchors.top: logo.bottom
765+ anchors.topMargin: units.gu(2)
766+ anchors.horizontalCenter: logo.horizontalCenter
767+ fontSize: "x-large"
768+ color: "#222222"
769+ MouseArea {
770+ anchors.fill: parent
771+ enabled: !flipable.flipped
772+ onClicked: {
773+ resetGame();
774+ }
775+ }
776+ }
777+ Label {
778+ id: bestscore
779+ property int bestsofar: 0
780+ text: "..."
781+ anchors.top: playbutton.bottom
782+ anchors.topMargin: units.gu(2)
783+ anchors.horizontalCenter: logo.horizontalCenter
784+ fontSize: "large"
785+ color: "#222222"
786+ function updateScore(score) {
787+ if (score >= bestscore.bestsofar) {
788+ bestscore.text = "Best score: " + score;
789+ bestscore.bestsofar = score;
790+ }
791+ }
792+ }
793+
794+ Rectangle {
795+ id: helpbutton
796+ width: units.gu(5)
797+ height: units.gu(5)
798+ radius: units.gu(1)
799+ Label {
800+ text: "?"
801+ anchors.centerIn: parent
802+ }
803+ anchors.top: parent.top
804+ anchors.right: parent.right
805+ anchors.margins: units.gu(2)
806+ MouseArea {
807+ anchors.fill: parent
808+ onClicked: {
809+ PopupUtils.open(helppop, helpbutton)
810+ }
811+ }
812+ }
813+ Component {
814+ id: helppop
815+ Popover {
816+ id: helpsheet
817+ Rectangle {
818+ anchors.top: parent.top
819+ anchors.left: parent.left
820+ anchors.right: parent.right
821+ height: units.gu(15)
822+ border.color: "#ddd"
823+ radius: 2
824+ Label {
825+ anchors.fill: parent
826+ anchors.margins: units.gu(1)
827+ text: "game by <a href='http://twitter.com/sil'>@sil</a><br>music: <a href='http://incompetech.com/music/royalty-free/index.html?isrc=USUAN1200076'>Easy Lemon</a> from Kevin McCloud (incompetech.com)<br>sounds: freesound from <a href='http://www.freesound.org/people/kantouth/sounds/106727/'>kantouth</a> / <a href='http://www.freesound.org/people/tictacshutup/sounds/407/'>tictacshutup</a> / <a href='http://www.freesound.org/people/dj-chronos/sounds/45137/'>dj-chronos</a> / <a href='http://www.freesound.org/people/justinbw/sounds/80921/'>justinbw</a>"
828+ wrapMode: Text.WordWrap
829+ textFormat: Text.StyledText
830+ onLinkActivated: Qt.openUrlExternally(link)
831+ }
832+ }
833+ }
834+ }
835+
836+ }
837+
838+ back: Rectangle {
839+ id: main
840+ color: "#58585A"
841+ width: flipable.width // 48 * 7 + 2 * 7 + 2
842+ height: flipable.height // 48 * 10 + 2 * 10 + 50
843+ property var selectedItems: []
844+ /*
845+ obtained with:
846+ import re; fp=open('/usr/share/dict/words');
847+ shorts=[re.sub('[^a-zA-Z]','',x.lower().strip()) for x in fp if len(x)<7];
848+ from collections import Counter; c=Counter(''.join(shorts)); least=c.most_common()[-1][1];
849+ print dict([(x.upper(),c[x]/least) for x in c])
850+ */
851+ property variant letterFreqs: {
852+ 'A': 66, 'C': 22, 'B': 19, 'E': 72, 'D': 28, 'G': 18, 'F': 12, 'I': 41, 'H': 20,
853+ 'K': 14, 'J': 4, 'M': 22, 'L': 40, 'O': 48, 'N': 35, 'Q': 1, 'P': 22, 'S': 75,
854+ 'R': 43, 'U': 26, 'T': 37, 'W': 13, 'V': 7, 'Y': 19, 'X': 3, 'Z': 4
855+ }
856+ property variant letterScores: {
857+ "A":1,"B":3,"C":3,"D":2,"E":1,"F":4,"G":2,"H":4,"I":1,"J":8,"K":5,"L":1,
858+ "M":3,"N":1,"O":1,"P":3,"Q":10,"R":1,"S":1,"T":1,"U":1,"V":4,"W":4,"X":8,
859+ "Y":4,"Z":10
860+ }
861+ property int score: 0
862+
863+ function getRandomWeightedLetter() {
864+ // could work out sumOfWeights once, but this is easier to understand
865+ var sumOfWeights = 0;
866+ for (var k in main.letterFreqs) { sumOfWeights += main.letterFreqs[k]; }
867+ var selection = Math.floor(Math.random() * sumOfWeights);
868+ for (var k in main.letterFreqs) {
869+ if (selection < main.letterFreqs[k]) return k;
870+ selection -= main.letterFreqs[k];
871+ }
872+ }
873+
874+ Audio {
875+ id: music
876+ source: "Easy_Lemon_60_second.ogg"
877+ autoPlay: true
878+ onStopped: music.play()
879+ muted: !volume.audible
880+ }
881+
882+ Audio {
883+ id: click
884+ source: "407__tictacshutup__click-1-off-click.ogg"
885+ autoLoad: true
886+ muted: !volume.audible
887+ }
888+
889+ Audio {
890+ id: success
891+ source: "80921__justinbw__buttonchime02up.ogg"
892+ autoLoad: true
893+ muted: !volume.audible
894+ }
895+
896+ Audio {
897+ id: failure
898+ source: "106727__kantouth__cartoon-bing-low.ogg"
899+ autoLoad: true
900+ muted: !volume.audible
901+ }
902+
903+ Audio {
904+ id: gameover
905+ source: "45137__dj-chronos__dark-church-bell.ogg"
906+ autoLoad: true
907+ onStopped: {
908+ flipable.flipped = false
909+ }
910+ muted: !volume.audible
911+ }
912+
913+ Rectangle {
914+ id: bottombar
915+ anchors.bottom: main.bottom
916+ anchors.left: main.left
917+ anchors.right: main.right
918+ height: (main.height - (game.squaresize * 10 + 11)) / 2
919+ z: 2
920+ color: Qt.hsla(210/360, 0.84, 0.2)
921+
922+ Rectangle {
923+ id: mainscore
924+ anchors {
925+ centerIn: bottombar
926+ bottom: bottombar.bottom
927+ }
928+ z: 2
929+ color: Qt.rgba(0,0,0,0)
930+ Label {
931+ anchors.centerIn: parent
932+ fontSize: "x-large"
933+ text: "" + main.score
934+ color: "white"
935+ }
936+ }
937+ }
938+
939+ Rectangle {
940+ id: topbar
941+ color: "blue"
942+ anchors.top: main.top
943+ anchors.left: main.left
944+ anchors.right: main.right
945+ height: (main.height - (game.squaresize * 10 + 11)) / 2
946+ z: 2
947+
948+ Rectangle {
949+ anchors.fill: topbar
950+ z: 2
951+ color: accum.text == "" ? "#efefea" : (accum.isValid ? Qt.hsla(124/360, 0.83, 0.45) : Qt.hsla(0, 0.83, 0.65))
952+ id: suggestedWordContainer
953+ Label {
954+ id: accum
955+ anchors.centerIn: parent
956+ text: ""
957+ color: "white"
958+ fontSize: "x-large"
959+ font.bold: true
960+ property bool isValid: false
961+ function findBinaryWord( word ) {
962+ var bd = Wordlist.wordlist;
963+ var l = word.length;
964+ // Don't search if there's nothing to look through
965+ if ( !bd[l] ) {
966+ return false;
967+ }
968+ // Get the number of words in the dictionary bin
969+ var words = bd[l].length / l,
970+ // The low point from where we're starting the binary search
971+ low = 0,
972+ // The max high point
973+ high = words - 1,
974+ // And the precise middle of the search
975+ mid = Math.floor( words / 2 );
976+ // We continue to look until we reach a final word
977+ while ( high >= low ) {
978+ // Grab the word at our current position
979+ var found = bd[l].substr( l * mid, l );
980+ // If we've found the word, stop now
981+ if ( word === found ) {
982+ return true;
983+ }
984+ // Otherwise, compare
985+ // If we're too high, move lower
986+ if ( word < found ) {
987+ high = mid - 1;
988+ // If we're too low, go higher
989+ } else {
990+ low = mid + 1;
991+ }
992+ // And find the new search point
993+ mid = Math.floor( (low + high) / 2 );
994+ }
995+ // Nothing was found
996+ return false;
997+ }
998+ onTextChanged: {
999+ accum.isValid = findBinaryWord(accum.text);
1000+ }
1001+ }
1002+ function processSuggestedWord() {
1003+ if (accum.isValid) {
1004+ success.play();
1005+ var thisscore = 0, wordlength = accum.text.length;
1006+ accum.text = "";
1007+
1008+ // tell the boxes to destroy themselves
1009+ main.selectedItems.forEach(function(b) {
1010+ thisscore += main.letterScores[b.containedLetter];
1011+ b.state = "dead";
1012+ })
1013+ main.selectedItems = [];
1014+ main.score += thisscore * wordlength;
1015+ scoredisplay.text = "" + (thisscore * wordlength);
1016+ showscoredisplay.start();
1017+ } else {
1018+ failure.play();
1019+ accum.text = "";
1020+ main.selectedItems.forEach(function(b) { b.selected = false; })
1021+ main.selectedItems = [];
1022+ }
1023+ }
1024+ focus: true
1025+ Keys.onReturnPressed: {
1026+ suggestedWordContainer.processSuggestedWord();
1027+ }
1028+ MouseArea {
1029+ anchors.fill: parent
1030+ onClicked: { suggestedWordContainer.processSuggestedWord(); }
1031+ }
1032+ }
1033+
1034+
1035+ }
1036+
1037+ Label {
1038+ id: scoredisplay
1039+ anchors.centerIn: parent
1040+ z: 3
1041+ fontSize: "x-large"
1042+ text: "200"
1043+ color: "red"
1044+ opacity: 0
1045+ }
1046+
1047+ ParallelAnimation {
1048+ id: showscoredisplay
1049+ NumberAnimation {
1050+ property: "scale"
1051+ from: 0.1
1052+ to: 8.0
1053+ duration: 400
1054+ target: scoredisplay
1055+ }
1056+ SequentialAnimation {
1057+ NumberAnimation {
1058+ property: "opacity"
1059+ from: 0
1060+ to: 1.0
1061+ duration: 20
1062+ target: scoredisplay
1063+ }
1064+ NumberAnimation {
1065+ property: "opacity"
1066+ from: 1.0
1067+ to: 0
1068+ duration: 380
1069+ target: scoredisplay
1070+ }
1071+ }
1072+ }
1073+
1074+
1075+ Timer {
1076+ id: droptimer
1077+ repeat: true
1078+ running: flipable.flipped
1079+ interval: 2000
1080+ property int tickCount: 0
1081+ triggeredOnStart: true
1082+ onTriggered: {
1083+ tickCount += 5
1084+ droptimer.interval = 2000 - tickCount
1085+ var idx = Math.round(Math.random() * (lm.count - 1));
1086+ lm.get(idx).letters.append({ letter: main.getRandomWeightedLetter() });
1087+ if (lm.get(idx).letters.count >= 10) {
1088+ droptimer.stop();
1089+ gameover.play();
1090+ var db = LocalStorage.openDatabaseSync("dropping-letters", "1.0", "Dropping Letters", 1000);
1091+ db.transaction(function(tx) {
1092+ tx.executeSql("insert into Scores values (?,?)", [main.score, 0]);
1093+ bestscore.updateScore(main.score);
1094+ });
1095+ }
1096+ }
1097+ }
1098+
1099+ ListModel {
1100+ id: lm
1101+ }
1102+
1103+
1104+ Rectangle {
1105+ id: game
1106+ property int squaresize: Math.min((flipable.width) / 7, (flipable.height - (flipable.minChromeHeight * 2)) / 10)
1107+ anchors.top: topbar.bottom
1108+ anchors.bottom: bottombar.top
1109+ anchors.left: main.left
1110+ anchors.right: main.right
1111+ scale: -1
1112+ color: "#efefea"
1113+ Row {
1114+ anchors.horizontalCenter: game.horizontalCenter
1115+ anchors.top: game.top
1116+ anchors.topMargin: 1
1117+ //spacing: 1
1118+ Repeater {
1119+ model: lm
1120+ Column {
1121+ //spacing: 1
1122+ property int idx: index
1123+ width: game.squaresize
1124+ height: game.height
1125+ add: Transition {
1126+ NumberAnimation { properties: "y"; easing.type: Easing.OutBounce; duration: 1000 }
1127+ }
1128+ move: Transition {
1129+ NumberAnimation { properties: "y"; easing.type: Easing.OutBounce }
1130+ }
1131+ Repeater {
1132+ model: letters
1133+ Rectangle {
1134+ id: box
1135+ property bool selected: false
1136+ property int idx: index
1137+ property string containedLetter: letter
1138+ color: {
1139+ if (lm.get(parent.idx).letters.count >= 10) {
1140+ return "red";
1141+ } else if (!selected) {
1142+ return Qt.hsla(210/360, 0.84, 0.25 + (idx * 0.05));
1143+ } else if (accum.isValid) {
1144+ return Qt.hsla(124/360, 0.83, 0.45); // "#93cc98";
1145 } else {
1146- if (box === main.selectedItems[main.selectedItems.length - 1]) {
1147- main.selectedItems.pop(main.selectedItems.length - 1);
1148- box.selected = false;
1149- accum.text = accum.text.substr(0, accum.text.length - 1);
1150+ return Qt.hsla(0, 0.83, 0.65); // "#cc9598"
1151+ }
1152+ }
1153+ //radius: "medium"
1154+ scale: -1
1155+ width: game.squaresize
1156+ height: game.squaresize
1157+ y: game.height + game.squaresize
1158+ z: 5
1159+ Label {
1160+ anchors.centerIn: parent
1161+ text: letter
1162+ fontSize: "large"
1163+ font.bold: true
1164+ color: "#ffffff"
1165+ }
1166+ MouseArea {
1167+ anchors.fill: parent
1168+ onClicked: {
1169+ if (!box.selected) {
1170+ box.selected = true;
1171+ accum.text += letter;
1172+ click.play();
1173+ main.selectedItems[main.selectedItems.length] = box;
1174+ } else {
1175+ if (box === main.selectedItems[main.selectedItems.length - 1]) {
1176+ main.selectedItems.pop(main.selectedItems.length - 1);
1177+ box.selected = false;
1178+ accum.text = accum.text.substr(0, accum.text.length - 1);
1179+ }
1180 }
1181 }
1182 }
1183- }
1184- Behavior on opacity {
1185- SequentialAnimation {
1186- ScriptAction { script: pulseEmitter.burst(1000); }
1187- NumberAnimation { properties:"opacity"; duration: 500 }
1188- ScriptAction { script: lm.get(box.parent.idx).letters.remove(box.idx); }
1189- }
1190- }
1191- states: [
1192- State { name: "alive" },
1193- State {
1194- name: "dead"
1195- PropertyChanges { target: box; opacity: 0 }
1196- }
1197- ]
1198- ParticleSystem {
1199- id: particles
1200- width: flipable.width / 2
1201- height: flipable.width / 2
1202- anchors.centerIn: parent
1203- clip: false
1204- ImageParticle {
1205- source: "redStar.png"
1206- alpha: 0
1207- colorVariation: 0.6
1208- }
1209- Emitter {
1210- id: pulseEmitter
1211- x: parent.width/2
1212- y: parent.height/2
1213- emitRate: 2000
1214- lifeSpan: 500
1215- enabled: false
1216- velocity: AngleDirection { magnitude: 256; angleVariation: 360; magnitudeVariation: 200; }
1217- size: parent.width / 8
1218- sizeVariation: 8
1219+ Behavior on opacity {
1220+ SequentialAnimation {
1221+ ScriptAction { script: pulseEmitter.burst(1000); }
1222+ NumberAnimation { properties:"opacity"; duration: 500 }
1223+ ScriptAction { script: lm.get(box.parent.idx).letters.remove(box.idx); }
1224+ }
1225+ }
1226+ states: [
1227+ State { name: "alive" },
1228+ State {
1229+ name: "dead"
1230+ PropertyChanges { target: box; opacity: 0 }
1231+ }
1232+ ]
1233+ ParticleSystem {
1234+ id: particles
1235+ width: flipable.width / 2
1236+ height: flipable.width / 2
1237+ anchors.centerIn: parent
1238+ clip: false
1239+ ImageParticle {
1240+ source: "redStar.png"
1241+ alpha: 0
1242+ colorVariation: 0.6
1243+ }
1244+ Emitter {
1245+ id: pulseEmitter
1246+ x: parent.width/2
1247+ y: parent.height/2
1248+ emitRate: 2000
1249+ lifeSpan: 500
1250+ enabled: false
1251+ velocity: AngleDirection { magnitude: 256; angleVariation: 360; magnitudeVariation: 200; }
1252+ size: parent.width / 8
1253+ sizeVariation: 8
1254+ }
1255 }
1256 }
1257 }
1258@@ -633,4 +615,17 @@
1259 }
1260 }
1261 }
1262+ HUD.HUD {
1263+ applicationIdentifier: "dropping-letters"
1264+ HUD.Context {
1265+ HUD.QuitAction {
1266+ onTriggered: Qt.quit()
1267+ }
1268+ HUD.Action {
1269+ label: i18n.tr("New game")
1270+ keywords: "Reset;Start;New"
1271+ onTriggered: resetGame()
1272+ }
1273+ }
1274+ }
1275 }
1276
1277=== modified file 'dropping-letters.qmlproject'
1278--- dropping-letters.qmlproject 2013-05-01 05:13:52 +0000
1279+++ dropping-letters.qmlproject 2013-05-07 05:11:28 +0000
1280@@ -3,7 +3,7 @@
1281 import QmlProject 1.1
1282
1283 Project {
1284- mainFile: "dropping-letters-ubuntu.qml"
1285+ mainFile: "dropping-letters.qml"
1286
1287 /* Include .qml, .js, and image files from current directory and subdirectories */
1288 QmlFiles {
1289
1290=== added file 'new.png'
1291Binary files new.png 1970-01-01 00:00:00 +0000 and new.png 2013-05-07 05:11:28 +0000 differ

Subscribers

People subscribed via source and target branches