Skip to content
This repository has been archived by the owner on Nov 30, 2022. It is now read-only.

Commit

Permalink
! core: Move extension classes to the package object #28
Browse files Browse the repository at this point in the history
* Tweaking and Snailing are now in `macroid._` and not in the Cakes.
* Implicit conversion to UiFuture is now in `macroid._` as well.
* Moved UiThreading.{runUi,getUi} to Ui.{run,get}.
  • Loading branch information
stanch committed May 1, 2015
1 parent f58c761 commit e60f532
Show file tree
Hide file tree
Showing 19 changed files with 123 additions and 145 deletions.
7 changes: 2 additions & 5 deletions macroid-core/src/main/scala/macroid/Cakes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ private[macroid] trait LayoutDsl
extends Searching
with LayoutBuilding
with FragmentBuilding
with Tweaking
with Snailing

object LayoutDsl extends LayoutDsl

Expand All @@ -22,10 +20,9 @@ private[macroid] trait ToastDsl
object ToastDsl extends ToastDsl

private[macroid] trait FullDsl
extends UiThreading
extends MediaQueries
with LayoutDsl with Tweaks with Snails
with ToastDsl with Loafs
with DialogDsl with Phrases
with MediaQueries
with ToastDsl with Loafs

object FullDsl extends FullDsl
4 changes: 0 additions & 4 deletions macroid-core/src/main/scala/macroid/Snailing.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ trait CanSnail[W, S, R] {
}

object CanSnail {
import UiThreading._

implicit def `Widget is snailable with Snail`[W <: View, S <: Snail[W]](implicit ec: ExecutionContext): CanSnail[W, S, W] =
new CanSnail[W, S, W] {
def snail(w: W, s: S) = s(w).withResultAsync(w)
Expand Down Expand Up @@ -92,5 +90,3 @@ private[macroid] trait Snailing {
def <~~[T, R](t: T)(implicit canSnail: CanSnail[W, T, R]): Ui[Future[R]] = canSnail.snail(w, t)
}
}

object Snailing extends Snailing
2 changes: 0 additions & 2 deletions macroid-core/src/main/scala/macroid/Snails.scala
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ private[macroid] trait BasicSnails {
}

private[macroid] trait ProgressSnails extends BasicSnails with VisibilityTweaks {
import UiThreading._

/** Show this progress bar with indeterminate progress and hide it once `future` is done */
def waitProgress(future: Future[Any])(implicit ec: ExecutionContext): Snail[ProgressBar] =
Tweak[ProgressBar](_.setIndeterminate(true)) + show ++ wait(future) + hide
Expand Down
2 changes: 0 additions & 2 deletions macroid-core/src/main/scala/macroid/Tweaking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,3 @@ private[macroid] trait Tweaking {
def <~[T, R](t: T)(implicit canTweak: CanTweak[W, T, R]): Ui[R] = canTweak.tweak(w, t)
}
}

object Tweaking extends Tweaking
86 changes: 83 additions & 3 deletions macroid-core/src/main/scala/macroid/UiActions.scala
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
package macroid

import android.os.Looper
import android.os.{Handler, Looper}

import scala.concurrent.{ExecutionContext, Future}
import scala.util.{ Failure, Success, Try }

/** An ExecutionContext associated with the UI thread */
object UiThreadExecutionContext extends ExecutionContext {
private lazy val uiHandler = new Handler(Looper.getMainLooper)

def reportFailure(t: Throwable) = t.printStackTrace()
def execute(runnable: Runnable) = uiHandler.post(runnable)
}

/** A UI action that can be sent to the UI thread for execution */
class Ui[+A](private[Ui] val v: () A) {
import macroid.UiThreading._

/** map combinator */
def map[B](f: A B) = Ui(f(v()))

Expand Down Expand Up @@ -56,4 +62,78 @@ object Ui {

/** Combine (sequence) several UI actions together */
def sequence[A](vs: Ui[A]*) = Ui(vs.map(_.v()))

/** Run a UI action on the UI thread */
def run[A](ui: Ui[A]) = ui.run

/** Run several UI actions on the UI thread */
def run[A](ui1: Ui[A], ui2: Ui[A], uis: Ui[A]*) = sequence(ui1 +: ui2 +: uis: _*).run

/** Get the result of executing an UI action on the current (hopefully, UI!) thread */
def get[A](ui: Ui[A]) = ui.get
}

/** Helpers to run UI actions as Future callbacks */
case class UiFuture[T](future: Future[T]) extends AnyVal {
private def applyUi[A, B](f: Function[A, Ui[B]]): Function[A, B] = x f(x).get
private def partialApplyUi[A, B](f: PartialFunction[A, Ui[B]]) = f andThen (_.get)

/** Same as map, but performed on the UI thread.
*
* If the future is already completed and the current thread is the UI thread,
* the UI action will be applied in-place, rather than asynchronously.
*/
def mapUi[S](f: Function[T, Ui[S]]) =
if (future.isCompleted && Ui.uiThread == Thread.currentThread) {
future.value.get.map(applyUi(f)) match {
case Success(x) Future.successful(x)
case Failure(t) Future.failed(t)
}
} else {
future.map(applyUi(f))(UiThreadExecutionContext)
}

/** Same as flatMap, but performed on the UI thread
*
* If the future is already completed and the current thread is the UI thread,
* the UI action will be applied in-place, rather than asynchronously.
*/
def flatMapUi[S](f: Function[T, Ui[Future[S]]]) = {
if (future.isCompleted && Ui.uiThread == Thread.currentThread) {
future.value.get.map(applyUi(f)) match {
case Success(x) x
case Failure(t) Future.failed(t)
}
} else {
future.flatMap(applyUi(f))(UiThreadExecutionContext)
}
}

/** Same as foreach, but performed on the UI thread
*
* If the future is already completed and the current thread is the UI thread,
* the UI action will be applied in-place, rather than asynchronously.
*/
def foreachUi[U](f: Function[T, Ui[U]]) =
if (future.isCompleted && Ui.uiThread == Thread.currentThread) {
future.value.get.foreach(applyUi(f))
} else {
future.foreach(applyUi(f))(UiThreadExecutionContext)
}

/** Same as recover, but performed on the UI thread */
def recoverUi[U >: T](pf: PartialFunction[Throwable, Ui[U]]) =
future.recover(partialApplyUi(pf))(UiThreadExecutionContext)

/** Same as onSuccess, but performed on the UI thread */
def onSuccessUi[U >: T](pf: PartialFunction[T, Ui[U]]) =
future.onSuccess(partialApplyUi(pf))(UiThreadExecutionContext)

/** Same as onFailure, but performed on the UI thread */
def onFailureUi[U](pf: PartialFunction[Throwable, Ui[U]]) =
future.onFailure(partialApplyUi(pf))(UiThreadExecutionContext)

/** Same as onComplete, but performed on the UI thread */
def onCompleteUi[U](pf: PartialFunction[Try[T], Ui[U]]) =
future.onComplete(partialApplyUi(pf))(UiThreadExecutionContext)
}
92 changes: 0 additions & 92 deletions macroid-core/src/main/scala/macroid/UiThreading.scala

This file was deleted.

6 changes: 6 additions & 0 deletions macroid-core/src/main/scala/macroid/package.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import scala.language.implicitConversions
import scala.concurrent.Future

package object macroid extends Tweaking with Snailing {
implicit def futureToUiFuture[A](future: Future[A]): UiFuture[A] = UiFuture(future)
}
3 changes: 1 addition & 2 deletions macroid-core/src/main/scala/macroid/util/Effector.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package macroid.util

import macroid.Ui
import macroid.UiThreading.UiFuture
import macroid._

import scala.language.higherKinds
import scala.concurrent.{ Future, ExecutionContext }
Expand Down
2 changes: 1 addition & 1 deletion macroid-core/src/main/scala/macroid/warts/CheckUi.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ object CheckUi extends WartTraverser {
case LabelDef(_, _, _)
case stat
if (stat.tpe != null && stat.tpe <:< typeOf[macroid.Ui[_]])
u.error(stat.pos, s"This statement returns an Ui action, but does not actually perform it. Call the `run` method, wrap it in `runUi`, or combine it with other Ui actions.")
u.error(stat.pos, s"This statement returns a UI action, but does not actually run it. Call the `run` method, or wrap it in `Ui.run`, or combine it with other UI actions.")
}

new u.Traverser {
Expand Down
6 changes: 3 additions & 3 deletions macroid-docs/guide/Bricks.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,15 @@ layout <~ addViews(button, textView, ...)

Finally, note that bricks return a [UI action](UiActions.html), therefore therefore
if you need to use them e.g. in `setContentView`,
`getUi` is required. *This also means that `w[Widget]` will
return a new (different) instance of `Widget` each time you call `getUi(w[Widget])` or `w[Widget].get`*.
`Ui.get` is required. *This also means that `w[Widget]` will
return a new (different) instance of `Widget` each time you call `Ui.get(w[Widget])` or `w[Widget].get`*.

```scala
override def onCreate(savedInstanceState: Bundle) = {
super.onCreate(savedInstanceState)

setContentView {
getUi {
Ui.get {
l[LinearLayout](
w[Button],
w[TextView]
Expand Down
13 changes: 7 additions & 6 deletions macroid-docs/guide/Imports.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,25 @@
Normally most *Macroid* features are imported via

```scala
import macroid._
import macroid.FullDsl._
```

However you can also import them one by one (click list items to jump to docs):

* `macroid` package object includes
* [`macroid.UiFuture` implicit conversion](UiActions.html)
* [`macroid.Tweaking` operators](Tweaks.html#tweaking) (standard tweaks should be imported separately from `macroid.Tweaks`)
* [`macroid.Snailing` operators](Snails.html#-snailing-) (standard snails should be imported separately from `macroid.Snails`)
* `macroid.LayoutDsl` includes
* [`macroid.Searching`](Searching.html)
* [`macroid.LayoutBuilding`](Bricks.html)
* [`macroid.FragmentBuilding`](Fragments.html)
* [`macroid.Tweaking`](Tweaks.html#tweaking) (standard tweaks should be imported separately from `macroid.Tweaks`)
* [`macroid.Snailing`](Snails.html#-snailing-) (standard snails should be imported separately from `macroid.Snails`)
* [`macroid.Transforming`](Transformers.html)
* [`macroid.DialogDsl`](ToastsDialogs.html#dialogs) (standard phrases should be imported separately from `macroid.Phrases`)
* [`macroid.ToastDsl`](ToastsDialogs.html#toasts)
* [`macroid.ToastDsl`](ToastsDialogs.html#toasts) (standard loafs should be imported separately from `macroid.Loafs`)
* `macroid.FullDsl` includes
* [`macroid.UiThreading`](UiActions.html)
* `macroid.LayoutDsl`, `macroid.Tweaks`, `macroid.Snails`
* `macroid.ToastDsl`
* `macroid.ToastDsl`, `macroid.Loafs`
* `macroid.DialogDsl`, `macroid.Phrases`
* [`macroid.MediaQueries`](MediaQueries.html)

Expand Down
3 changes: 1 addition & 2 deletions macroid-docs/guide/Snails.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,7 @@ val fadeAndDisappear =
## “Snailing”

“Snailing” is a made-up word that denotes the process of applying snails to widgets.
Snailing is very similar to [tweaking](Tweaks.html#tweaking), except it uses the `<~~` operator
(see [Changelog](../Changelog.html) for the recent syntax changes):
Snailing is very similar to [tweaking](Tweaks.html#tweaking), except it uses the `<~~` operator:

```scala
textView <~~ fadeIn
Expand Down
12 changes: 6 additions & 6 deletions macroid-docs/guide/ToastsDialogs.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,17 @@ The toast API is similar to [tweaking](Tweaks.html#tweaking) and consists of `to
The loafs are as follows:

```scala
runUi {
Ui.run {
// `fry` shows the toast
toast("Foo") <~ fry
}

runUi {
Ui.run {
// `long` makes the toast long
toast("Foooo") <~ long <~ fry
}

runUi {
Ui.run {
// `gravity` should be self-evident
toast("XY") <~ gravity(Gravity.CENTER, xOffset = 3 dp) <~ fry
}
Expand All @@ -42,15 +42,15 @@ The dialog API is also similar to [tweaking](Tweaks.html#tweaking) and consists
It is also possible to provide a dialog theme:

```scala
runUi {
Ui.run {
dialog(themeId)("I’m a message")
}
```

The phrases can be discovered via the [scaladoc](../api/core/macroid/Phrases$.html), here are some examples:

```scala
runUi {
Ui.run {
dialog("Please fasten your seat belts") <~
title("Warning") <~
speak // this shows the dialog
Expand All @@ -65,7 +65,7 @@ Finally, two implicit conversions to `Dialog.OnClickListener` are awailable:
Example:

```scala
runUi {
Ui.run {
dialog("Are you sure") <~
positiveYes(textView <~ show) <~
negativeNo(textView <~ hide) <~
Expand Down
Loading

0 comments on commit e60f532

Please sign in to comment.