#776 Using FWT to make a web base "desktop" app.

tcolar Fri 9 Oct 2009

I'm working on a Fan project.

I want the frontend to be web-based even though it needs to be a lot like a desktop app (ie: like a "swing" app, with buttons etc...).

Anyway, I figure FWT would work well here to write that frontend, what I'm not 100% clear about is making backend calls from FWT (ie AJAX) .

Is is the way to do it (is it up to date): http://fandev.org/sidewalk/topic/469

Does using this (WebappClient) together with FWT sound like the right approach.

Is there any example/code that does things like that I can look at.

Thanks.

andy Fri 9 Oct 2009

That example is about right - except most of webappClient got folded into dom - but yes, that is what you want to do:

Button
{
  text = "Click me!"
  onAction.add
  {
    HttpReq(`/echo`).send("This is from the server!") |res|
    {
      field.text = res.content
    }
  }
}

I added a full example to hg: changeset

tcolar Fri 9 Oct 2009

Sounds good ! Thanks much for taking the time to write an example.

tcolar Fri 9 Oct 2009

It works on my laptop (32 bits). However I can't seem to get this to work on my Linux 64 bits.

Even the standard example: graphics.fwt juts shows a blank page. Tried both in Chromium & Firefox 3.5+.

When I do view source, I can see the html / javascript and that looks ok .. but all I get is a white page.

Is this a 64 bits libraries thing ? What can I do to make it work ?

Thanks.

andy Fri 9 Oct 2009

Hard to say without being able to test. Do other examples work? Like the examples/web ones? Is it just the <canvas> that is not working?

tcolar Fri 9 Oct 2009

For hello.fwt I get a blank page too. (Firefox 3.5.3)

In the javascript console I get:

  • m is undefined fwt.js:4533
  • grid is undefined show-script?hello.fwt:40

Generated source looks like this:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns='http://www.w3.org/1999/xhtml'>
<head>
<title>FWT Demo - hello.fwt</title>
<script type='text/javascript' src='/sys/pod/sys/sys.js'></script>
<script type='text/javascript' src='/sys/pod/gfx/gfx.js'></script>
<script type='text/javascript' src='/sys/pod/fwt/fwt.js'></script>
<style type='text/css'>
body { font: 10pt Arial; }
a { color: #00f; }
</style>
<script type='text/javascript'>
fan.hello = {};
fan.hello.$pod = fan.sys.Pod.$add('hello');
with (fan.hello.$pod)
{
  var $0=$at('Hello','fwt::Window',[])
  $0.$am('make',4100);
}
fan.hello.Hello = fan.sys.Obj.$extend(fan.fwt.Window);
fan.hello.Hello.prototype.$ctor = function() { this.peer = new fan.fwt.WindowPeer(this); }
fan.hello.Hello.$type = fan.sys.Type.find("hello::Hello");
fan.hello.Hello.prototype.type = function() { return fan.hello.Hello.$type; }
fan.hello.Hello.make = function()
{
  var $this = this;
  var instance = new fan.hello.Hello();
  instance.make$();
  return instance;
}
fan.hello.Hello.prototype.make$ = function()
{
  var $this = this;
  fan.fwt.Window.prototype.make$.call(this, null, null);
  var grid = fan.fwt.GridPane.make();
  grid.numCols$(10);
  fan.sys.Int.times(200,fan.sys.Func.make(function(i) {
    grid = $this.m_grid$0;
    grid.add(fan.fwt.Button.make(fan.sys.Func.make(function(it) {
      i = $this.m_i$0;
      it.text$(fan.sys.Obj.toStr(i));
      return;
    },[new fan.sys.Param("it","fwt::Button",false)],fan.sys.Type.find("sys::Void"))));
    return;
  },[new fan.sys.Param("i","sys::Int",false)],fan.sys.Type.find("sys::Void")));
  var scrollPane = fan.fwt.ScrollPane.make(fan.sys.Func.make(function(it) {
    grid = $this.m_grid$0;
    it.content$(grid);
    it.hbar().onModify().add(fan.sys.Func.make(function(evt) {
      fan.sys.Obj.echo(("hbar = " + evt.data()));
      return;
    },[new fan.sys.Param("evt","fwt::Event",false)],fan.sys.Type.find("sys::Void")));
    it.vbar().onModify().add(fan.sys.Func.make(function(evt) {
      fan.sys.Obj.echo(("vbar = " + evt.data()));
      return;
    },[new fan.sys.Param("evt","fwt::Event",false)],fan.sys.Type.find("sys::Void")));
    return;
  },[new fan.sys.Param("it","fwt::ScrollPane",false)],fan.sys.Type.find("sys::Void")));
  return;
}
var hasRun = false;
var shell  = null;
var doLoad = function()
{
  // safari appears to have a problem calling this event
  // twice, so make sure we short-circuit if already run
  if (hasRun) return;
  hasRun = true;

  // load fresco
  shell = fan.hello.Hello.make();
  shell.open();
}
var doResize = function() { shell.relayout(); }
if (window.addEventListener)
{
  window.addEventListener('load', doLoad, false);
  window.addEventListener('resize', doResize, false);
}
else
{
  window.attachEvent('onload', doLoad);
  window.attachEvent('onresize', doResize);
}
</script>
</head>
<body>

</body>
</html>

tcolar Fri 9 Oct 2009

And all the examples in web/ work fine.

KevinKelley Sat 10 Oct 2009

I might be wrong, but I thing GridPane is one of the FWT widgets that isn't implemented yet in JS, which would explain one of those errors.

tcolar Sat 10 Oct 2009

works on my 32 bit ubuntu laptop.

andy Tue 13 Oct 2009

Hmm, not sure whats going on there - almost seems like the JS parser/VM is behaving differently, since the code should be the exact same. Though I don't have a 64-bit machine to test on. Is there another 64-bit browser you can test with to see if it behaves the same way?

katox Wed 6 Jan 2010

Thanks for the example. Do you think that using fantom serialization syntax (over HttpReq and back) is reasonable to make transparent fan remote calls? Parsing the data forth and back seems efficient but rather low level (a matter of taste, sure). Or what are your intentions in this regard? Simplied REST requests only?

Another scenario: "remote fs" where user would be able to manipulate contents of files stored on the server (of course not sending the whole content) - I't be nice to be able to use File types etc. instead of recoding everything into strings or duplicating the io APIs (using serialized holders wrapped in AJAX requests)...

brian Thu 7 Jan 2010

Using Fan serialization over a REST API seems like a good design to me. For SkyFoundry we use a customized souped up format we call Zinc (kind of like cross between CSV and google visualization JSON and Fantom serialization). That design works really well. I'm a big believer in treating your wire protocol as the most important interface between subsystems, and spending time to make sure it is clean, solid, and well documented. So I really like a set of REST URIs that consume and publish some standard format (Fantom, JSON, etc).

Login or Signup to reply.