Can I create a StackLayout with F# in Xamarin Forms? - f#

In his Xamarin 3 F# Awesomeness blog posting, Dave Thomas shows a StackLayout being created:
StackLayout.Create(
[
Entry (Placeholder = "Username")
Entry (Placeholder = "Password", IsPassword = true)
Button (Text = "Login", TextColor = Color.White, BackgroundColor = Color.FromHex "77D065")
],...
However, I get an error The field, constructor or member 'Create' is not defined
If I take out the .Create before the parens, the error message changes to The member of object constructor 'StackLayout' takes 0 arguments but is here given 1.
My interpretation is that Create is a static method taking a tuple but I can't see it defined anywhere in the assembly browser.
I'm also a bit bothered in his sample by a lowercase create being used on TabbedPage so it looks like the code is inconsistent and possibly typed in without compiling, although he shows screenshots below.
I will take any suggestion on how to do this - if there's no missing magical extension to add Create, I'm happy to take other approaches.
The parent class declaration, in the docs, is:
[Xamarin.Forms.ContentProperty("Children")]
public abstract class Layout<T> : Layout, IViewContainer<T>
where T : Xamarin.Forms.View
I figure there's an idiom for setting that content Children property available as initialisation to C# but maybe not to F#?
For example, I've got compiling code from the Forms Gallery sample such as:
StackLayout stackLayout = new StackLayout
{
Spacing = 0,
VerticalOptions = LayoutOptions.FillAndExpand,
Children =
{
new Label
{
Text = "StackLayout",
HorizontalOptions = LayoutOptions.Start
},
I tried transcribing this into F# syntax - the following is legal syntax but with the Children property being set gets the error The member of object constructor 'StackLayout' has no argument or settable return property 'Children'. The required signature is StackLayout(): unit. (FS0495)
type App() =
static member GetMainPage =
let sl = new StackLayout (
Spacing = 20,
VerticalOptions = LayoutOptions.FillAndExpand,
Children = [
new Label (Text = "StackLayout");
new Label (Text = "SuckLayout")
]
)
new ContentPage( Content = sl )
So, consider me baffled - I have no idea why Children is accessible from C# and not F#. The only suspicion that makes sense is that somehow the XAML content property annotation makes a difference to one compiler and not the other.
I am a very experienced C++ and Python programmer learning F# (and Swift) so I'm likely to stumble over syntax. I get tuples, I'm getting used to the weird role of commas and thankfully my Python background made me relaxed about whitespace.
Someone else had the same question in the comments on the blog post but no answer was posted.
I am using Xamarin.iOS 8.10.0.258
Xamarin.Forms 1.4.0.6341
My fallback is to give up on F# for GUI and use C# there with F# for main logic but I really liked the much more compact syntax.
Note that I also asked this over on their forums

The C# code you show is using the collection initialisers feature of C#, it will compile to calls to Children.Add(), it is not setting the Children property.
F# does not have the same collection initialisers feature so you will have to manually call Children.Add() which is most likely what the StackLayout.Create function is doing.
The StackLayout.Create function could be a local function defined in a module called StackLayout which would explain why you don't see it in the docs.

Thanks to Leaf Garland above, here's the fixed function - I just needed that hint that C# was using Add.
type App() =
static member GetMainPage =
let sl = new StackLayout (
Spacing = 20.0,
VerticalOptions = LayoutOptions.FillAndExpand
)
let slc : View[] = [|
new Entry (Placeholder = "Username")
new Entry (Placeholder = "Password", IsPassword = true)
new Button (Text = "Login", TextColor = Color.White, BackgroundColor = Color.FromHex "77D065")
|]
slc |> Seq.iter (fun vc -> sl.Children.Add(vc))
new ContentPage( Content = sl )
Note that I had to create the temp variable slc to get an array typed as the superclass View
If you had all the same types you could use a literal list and slightly simpler code like this:
type App() =
static member GetMainPage =
let sl = new StackLayout (
Spacing = 20.0,
VerticalOptions = LayoutOptions.FillAndExpand
)
[
new Label (Text = "StackLayout")
new Label (Text = "SuckLayout")
] |> Seq.iter (fun vc -> sl.Children.Add(vc))
new ContentPage( Content = sl )
Here's a nice article about the C# collection initialisers if you're interested.
I"m too busy tonight but will try to write the StackLayout.Create function as an exercise.

Related

How to dynamically calculate value of a column in edit mode in Vaadin 8 grid component

I have a table in my UI which is a grid component. The value of one of the columns is calculated based on other column input values. So for example there are columns A,B, and C and value of column C = A + B. What I want to achieve is when user edits a row, before clicking the save button it would be able to see the calculated value in edit mode.
Is there a possibility to do that without Javascript or if not, how can I run a javascript snippet on onBlur event of the editable columns (A and B).
The calculated column is not editable by user.
This is a non functional code snippet that shows more or less what is the basis of my code.
Grid<Numbers> grid = new Grid<>(Numbers.class, false);
Editor<Numbers> editor = grid.getEditor();
Grid.Column<Numbers> aColumn = grid
.addColumn(Numbers::getA).setHeader("A")
.setWidth("120px").setFlexGrow(0);
Grid.Column<Numbers> bColumn = grid.addColumn(Numbers::getB)
.setHeader("B").setWidth("120px").setFlexGrow(0);
Grid.Column<Numbers> cColumn = grid.addColumn(Numbers::getC)
.setHeader("C");
Grid.Column<Numbers> editColumn = grid.addComponentColumn(numbers -> {
Button editButton = new Button("Edit");
editButton.addClickListener(e -> {
if (editor.isOpen())
editor.cancel();
grid.getEditor().editItem(numbers);
});
return editButton;
}).setWidth("150px").setFlexGrow(0);
Binder<Numbers> binder = new Binder<>(Numbers.class);
editor.setBinder(binder);
editor.setBuffered(true);
TextField aField = new TextField();
aField.setWidthFull();
binder.forField(aField)
.bind(Numbers::getA, Numbers::setA);
aColumn.setEditorComponent(aField);
TextField bField = new TextField();
bField.setWidthFull();
binder.forField(bField)
.bind(Numbers::getB, Numbers::setB);
bColumn.setEditorComponent(bField);
TextField cField = new TextField();
cField.setWidthFull();
binder.forField(cField)
.bind(Numbers::getC, Numbers::setC);
cColumn.setEditable(false);
Button saveButton = new Button("Save", e -> editor.save());
Button cancelButton = new Button(VaadinIcon.CLOSE.create(),
e -> editor.cancel());
cancelButton.addThemeVariants(ButtonVariant.LUMO_ICON,
ButtonVariant.LUMO_ERROR);
HorizontalLayout actions = new HorizontalLayout(saveButton,
cancelButton);
actions.setPadding(false);
editColumn.setEditorComponent(actions);
I tried adding this to some events:
Page.getCurrent().getJavaScript().execute("alert('the calculated value')");
The problem is the elements that are created by grid component have no id, so accessing them directly is not an option. Traversing dom is not really feasible too.

jetpack compose lazy custom layout

I tried to develop a simple custom layout just like the documentation
#Composable
fun MyBasicColumn(
modifier: Modifier = Modifier,
content: #Composable () -> Unit
) {
Layout(
modifier = modifier,
content = content
) { measurables, constraints ->
// Don't constrain child views further, measure them with given constraints
// List of measured children
val placeables = measurables.map { measurable ->
// Measure each children
measurable.measure(constraints)
}
// Set the size of the layout as big as it can
layout(constraints.maxWidth, constraints.maxHeight) {
// Track the y co-ord we have placed children up to
var yPosition = 0
// Place children in the parent layout
placeables.forEach { placeable ->
// Position item on the screen
placeable.placeRelative(x = 0, y = yPosition)
// Record the y co-ord placed up to
yPosition += placeable.height
}
}
}
}
it works fine when I know exact number of items
but what about lazy items?
there is nothing in documentation about how can I develop a LazyCustomLayout
You don't exactly have to know how many items are in the Layout, since even for dynamic lists, there's always a 'current number of items' which can be computed. Let's say you download a list of texts from a server, and then intend to use this Layout to render those. Even in that case, while the server may vary the length of the list, i.e., the list is dynamic in size, you would presumably have a LiveData object keeping track of the list items. From there, you can easily use the collectAsState() method inside a Composable, or the observeAsState() method tied to a LifecycleOwner to convert it into the Compose-compatible MutableState<T> variable. Hence, whenever the LiveData notifies a new value (addition, or deletion), the MutableState<T> variable will also be updated to reflect those values. This, you can use inside the said Layout, which is also a Composable and hence, will update along-side the server-values, in real-time.
The thing is, no matter how you get your list, in order to show it on-screen, or use it anywhere in your app, you would always have a list object, which can be exploited using Compose's declarative and reactive nature.

AwesomeWM, vicious BTC widget does not update textbox

I am trying to create a widget which displays the current BTC price, but the displayed widget is not updated.
First i create and register the widget with
mytextwidget = {
widget = wibox.widget.textbox,
}
btcbox = {
{
mytextwidget,
halign = "center",
layout = wibox.container.place,
},
forced_width = 100,
layout = wibox.layout.stack,
}
vicious.register(mytextwidget,vicious.contrib.btc,"$1",2,"eur")
I changed the btc widgets code, such that the indices of the returned table are numeric instead of the string "{price}", because i was not able to register it that way(maybe someone can tell me how to change the format string so that it works). I then add it to the default wibox with
s.mywibox:setup {
layout = wibox.layout.align.horizontal,
{ -- Left widgets
layout = wibox.layout.fixed.horizontal,
mylauncher,
s.mytaglist,
s.mypromptbox,
},
s.mytasklist,
-- Middle widget
{ -- Right widgets
layout = wibox.layout.fixed.horizontal,
mykeyboardlayout,
wibox.widget.systray(),
mytextclock,
batbox,
btcbox,
s.mylayoutbox,
}
}
As you can see there is also the batbox, which is setup the same way, except for registering to the vicious.widget.bat widget and this one works fine. I am new to awesomewm and especially Lua but i tried to reverse engineer through the vicious library and the text of the widget actually gets updated. I created a naughty notification which is called right after
widget.text = fmtd_data
in the init.lua of the vicious library, to display the widgets text and it is actually showing the correct result. Hopefully someone can explain it to me why this change is not displayed, thanks in advance!
Random guess without much time to try things out:
mytextwidget = {
widget = wibox.widget.textbox,
}
replace the above with
mytextwidget = wibox.widget.textbox()
Why am I suggesting this? Well, it's what is done on https://vicious.readthedocs.io/en/latest/examples.html#date-widget.

How to set element positions with dart_web_toolkit?

im new to dart and need to create a dynamic dom structure.
At the moment i use the dart_web_toolkit but im missing a position function for elements.
You can set the width and height but no position(top, left) for the element.
Am i missing something there, or can someone give me a solution for this?
Edit:
I have a Label with 2 Buttons in it and i can edit the Buttons size with "setPixelSize"
but i need something like "b1.setPosition" and i cant find this method to position my elements (in this example buttons).
At the moment they are just put after another in relation to the Label.
final el = querySelector('#body');
ui.Label rootLabel = new ui.Label();
rootLabel.addStyleName("root-style");
ui.RootPanel.get().addWidget(rootLabel, el);
final el1 = querySelector('.root-style');
ui.Button b1 = new ui.Button();
b1.text = "Button 1";
b1.addStyleName("b1");
b1.setPixelSize(30, 50);
ui.RootPanel.get().addWidget(b1, el1);
ui.Button b2 = new ui.Button();
b2.text = "Button 2";
b2.addStyleName("b2");
b1.setPixelSize(60,60);
ui.RootPanel.get().addWidget(b2, el1);
I just looked a bit thought the code of https://github.com/akserg/dart_web_toolkit/blob/afac7aac09e3bcd9d3e1f926815eb85040e46e07/lib/src/ui/button_base.dart
and it seems you should get the Element instance by calling b1.getElement() which should allow to access all elements properties like
b1.getElement().style
..position = 'absolute'
..top = '10px'
..left = '25px';
not tested though.

FlyoutNavigation - Views using NSLayoutConstraints Fail to Properly Draw View on First Use

Cross-posted here and on github, https://github.com/Clancey/FlyoutNavigation/issues/29.
I have an odd behavior I'm trying to track down, and I'm not sure if it is FlyoutNavigation or something else I'm doing. Maybe someone can take a quick look who understands things a bit better than I do.
Example Project - https://github.com/benhysell/FlyoutNavigationWithNSLayoutConstraintsError
Goal - Use https://gist.github.com/praeclarum/5175100, A C# syntax for NSLayoutConstraints, described in this blog post, http://praeclarum.org/post/45690317491/easy-layout-a-dsl-for-nslayoutconstraint, with FlyoutNavigation.
Issue - On the first use of a view that incorporates NSLayoutConstraints the view doesn't respect the constraints or background color, both odd. On subsequent 'selections' of the view from the menu of FlyoutNavigation the view will properly draw.
Setup - Working in Xamarin Beta Channel against iPhone Simulator 6.1 and the latest released Xcode.
Steps to Reproduce
1. The easiest way to show this is to open the sample project that comes with FlytoutNavigation and modify this project using the steps below. I included in this post a link to the example project I modified to show the error.
Add the gist, https://gist.github.com/praeclarum/5175100, to a new class, call it layout.
Add a new UIViewController and the following to ViewDidLoad(), note this was modified from the Xamarin 'Hello World' sample app one can create in VS2012
public override void ViewDidLoad()
{
base.ViewDidLoad();
View.Frame = UIScreen.MainScreen.Bounds;
View.BackgroundColor = UIColor.Red;
button = UIButton.FromType(UIButtonType.RoundedRect);
button.SetTitle("Click me", UIControlState.Normal);
button.TouchUpInside += (object sender, EventArgs e) =>
{
button.SetTitle(String.Format("clicked {0} times", numClicks++), UIControlState.Normal);
};
View.AddSubview(button);
const int ButtonWidth = 75;
const int HPadding = 22;
const int VPadding = 44;
View.ConstrainLayout(() =>
button.Frame.Width == ButtonWidth &&
button.Frame.Left == View.Frame.Left + HPadding &&
button.Frame.Top == View.Frame.Top + VPadding);
}
In the MainController.cs replace
navigation.ViewControllers = Array.ConvertAll (Tasks, title =>
new UINavigationController (new TaskPageController (navigation, title))
);
with
navigation.ViewControllers = Array.ConvertAll(Tasks, title =>
new UINavigationController(new MyViewController(navigation))
);
I'm saying 'make every view a view that implements NSLayoutConstraints'.
Run Application, first view returns with:
Select the same item from the FlyoutNavigation menu and it will then properly draw.
I've traced through FlyoutNavigationController.cs a couple of times and it appears on the second time selecting the item from FlyoutNavigation, on line 238:
this.View.AddSubview (mainView);
ViewControllers[0].ChildViewControllers[0].View.Frame {{X=160,Y=208,Width=0,Height=0}} System.Drawing.RectangleF
This is the incorrect size for the view, however after I step over line 238:
ViewControllers[0].ChildViewControllers[0].View.Frame {{X=0,Y=0,Width=320,Height=416}} System.Drawing.RectangleF
The position is fixed, and the view will draw correctly.
Summary - I've tried using the gist with the NSLayoutConstraints by itself in a single page window application without issue, and I'm thinking since it does eventually draw properly after a second invokation of FlyoutNavigation I'm thinking there is a 'something' I'm missing with the FlyoutNavigation, or setting incorrectly that I can't put my finger on.

Resources