xebia-functional / macroid

A modular functional UI language for Android
527 stars 37 forks source link

enhancement: access to layoutparams after lp tweak #34

Open pfn opened 10 years ago

pfn commented 10 years ago

many layout params require field changes instead of only constructor parameters (e.g. gravity, span, relative layout stuff, etc. [yes, there's RuleRelativeLayout, but that's besides the point]).

I don't have a good solution for this yet, but maybe something like an optional parameter set, e.g.

lp[A](...)(Map(field -> value))

or

lp[A](...) { p: A.LayoutParams(????) => p.... } // I don't see how this is possible

Alternatively, figure out a good way of creating AttributeSets so that one can do lp[A](Map(...).toAttributeSet)

etc.

pfn commented 10 years ago

setting layout margins is definitely a pain point

currently I have to run: lp + margins

where margins is something ugly like tweak { v: View => v.getLayoutParams.asIntanceOf[ViewGroup.MarginLayoutParams] ...}

and it requires having set lp beforehand

stanch commented 10 years ago

Yeah, I actually avoid using margins, replacing them with padding where possible :) From your suggestions, I think AttributeSets are the way to go, as it will also solve #30. Unfortunately, I don’t have any time to work on this now. Would you or @dant3 be interested in looking into AttributeSet stuff?

dant3 commented 10 years ago

After some experiments with macroid, I too feel lp as a pain point (if not using xml layouts :stuck_out_tongue:). AttributeSet api is definitely way to go to solve this, and similar issues. Not sure if I have enough time currently, but I could look at it later, if nobody will pick it up before.

stanch commented 10 years ago

@dant3 I think we can agree that Android’s LayoutParams API itself is a bit of a mess ;)

stanch commented 10 years ago

By the way, how much is it desired to change layout params after layout? If they are only set once, maybe widgets could be wrapped into something like this before being added to the layout:

// isParams is generated by an implicit macro
case class LP[L <: ViewGroup, +W <: View, X](view: W, params: X)(implicit isParams: IsParams[X, L])

I guess this could offer more type safety if done right, but not sure about the details yet. What do you think?

pfn commented 10 years ago

I don't understand how this would streamline the lp generation?

isParams only links VG and VG.LP?

Sent from my phone On Aug 17, 2014 3:38 AM, "Nick" notifications@github.com wrote:

By the way, how much is it desired to change layout params after layout? If they are only set once, maybe widgets could be wrapped into something like this before being added to the layout:

// isParams is generated by an implicit macrocase class LP[L <: ViewGroup, +W <: View, X](view: W, params: X)(implicit isParams: IsParams[X, L])

I guess this could offer more type safety if done right, but not sure about the details yet. What do you think?

— Reply to this email directly or view it on GitHub https://github.com/macroid/macroid/issues/34#issuecomment-52418618.

pfn commented 10 years ago

Oh, I suppose the case class can take a function to operate on the params.

Rather than params: X it should be params: (X => Unit)?

pfn commented 10 years ago

Also, interestingly, it'd be nice if WindowManager.LayoutParams could be supported as well. It isn't a view group... But one applies the layout params to content views..

stanch commented 10 years ago

Sorry for not being elaborate enough. To give an example, if we wanted to add a Button to a LinearLayout, we would wrap it into

LP[LinearLayout, Button, LinearLayout.LayoutParams](w[Button], new LinearLayout.LayoutParams(...))

although your idea with X ⇒ Unit works as well. The way isParams works is that there will be automatic implicits for these relations:

implicitly[IsParams[LinearLayout.LayoutParams, LinearLayout]]
implicitly[IsParams[ViewGroup.LayoutParams, LinearLayout]]
...

Does this make sense?

To deal with WindowManager.LayoutParams, we could provide an alternative version of setContentView (also see #22) that takes something like Ui[LP[View]] (LP will need to drop the L <: ViewGroup restriction).

stanch commented 10 years ago

I don’t understand how this would streamline the lp generation?

Oh well, this is somewhat orthogonal, but I thought extra type safety might help to deal with this horrible API. As far as configuring the actual params is concerned, AttributeSet still seems the way to go.

pfn commented 10 years ago

This is basically what I came up with for my own thing.

  abstract class LpRelation[V <: ViewGroup, LP <: ViewGroup.LayoutParams : ClassTag] {
    def lpType = implicitly[ClassTag[LP]].runtimeClass
    def lp(args: Any*) = lpType.getConstructor(
      args map { a =>
        val c = a.getClass
        primitiveMap.getOrElse(c, c)
      }:_*).newInstance(args map (_.asInstanceOf[AnyRef]): _*).asInstanceOf[LP]
  }
  implicit object LLRelation extends LpRelation[LinearLayout, LinearLayout.LayoutParams]
  implicit object TRRelation extends LpRelation[TableRow, TableRow.LayoutParams]
pfn commented 10 years ago

Oh, can't forget the rest.

  def lp2[V <: ViewGroup,LP <: ViewGroup.LayoutParams, C](args: Any*)
                                                         (p: LP => C)
                                                         (implicit r: LpRelation[V,LP]) = tweak {
    v: View =>
      val lp = r.lp(args: _*)
      p(lp)
      v.setLayoutParams(lp)
  }