autopsy of a widget

87
Autopsy of a Widget The Internals of a qooxdoo Widget by Fabian Jakobs @fjakobs

Upload: fabian-jakobs

Post on 18-Jan-2015

2.680 views

Category:

Technology


3 download

DESCRIPTION

With modern JavaScript frameworks like ExtJS, SproutCore or qooxdoo, it is possible to create very rich user interfaces using only open web standards. But how do they do it? How do they work internally?In this talk I will open the thorax of a simple qooxdoo widget and look at the various layers and building blocks used to implement it. You will learn how the widget is represented in the DOM, how events are handled, and how the layout engine works. Often there is more than a single way to achieve something. In those cases the different options and qooxdoo's specific design decision will be presented.

TRANSCRIPT

  • 1. Autopsy ofa WidgetThe Internals of aqooxdoo Widget by Fabian Jakobs@fjakobs

2. Web Application 3. Browser as Render Engine 4. Primitives Image Text Rectangle 5. Tree of Primitives DOM tree Events CSS for styling JavaScript for behavior 6. The Body Widget 7. The WidgetSpinner Number input Composed widgetnew qx.ui.form.Spinner(20); 8. Static Structure: Composed Widgets 9. GoalCreate complex Widgets by composing WidgetsUse known GUI Concepts Abstraction over the Browser 10. GoalCreate complex Widgets by composing WidgetsUse known GUI Concepts Abstraction over the Browser 11. qx.Class.define("qx.ui.form.Spinner", { extend : qx.ui.core.Widget,construct : function(value) { this.base(arguments); } }); 12. qx.Class.define("qx.ui.form.Spinner", { extend : qx.ui.core.Widget,construct : function(value) { this.base(arguments);var layout = new qx.ui.layout.Grid(); layout.setRowFlex(0, 1); layout.setRowFlex(1, 1); layout.setColumnFlex(1, 1); this._setLayout(layout); } }); 13. qx.Class.define("qx.ui.form.Spinner", { extend : qx.ui.core.Widget,construct : function(value) { this.base(arguments);var layout = new qx.ui.layout.Grid(); layout.setRowFlex(0, 1); layout.setRowFlex(1, 1); layout.setColumnFlex(1, 1); this._setLayout(layout);this._input = new qx.ui.form.TextField(value.toString()); this._add(this._input, {row: 0, column: 0, rowSpan: 2});this._buttonUp = new qx.ui.form.Button(); this._buttonUp.setIcon("decoration/arrows/up-small.png"); this._add(this._buttonUp, {row: 0, column: 1});this._buttonDown = new qx.ui.form.Button(); this._buttonDown.setIcon("decoration/arrows/down-small.png"); this._add(this._buttonDown, {row: 1, column: 1}); } }); 14. Demo http://demo.qooxdoo.org/current/playground 15. DOM Structure 16. DOM Structure new qx.ui.form.TextField("Anatomy"); 17. DOM Structurenew qx.ui.form.TextField("Anatomy");

...

18. Container

...

19. Container Decorator

...

20. Container DecoratorContent

...

21. Summary 2-3 DOM elements Absolute positioning Fixed sizes No explicit padding

...

22. DOM StructureProCon Box Model Independence 2-3 DOM Elements Flexible StylingSize Computation in JS No Cross Browser Issues 23. Theming 24. The Guts: Working With The DOM 25. new qx.ui.form.TextField("Anatomy"); ???

...

26. The DOM is a Mess Many cross browser issues IEelement.style.filter ="alpha(opacity=" + (opacity * 100) + ");"; otherselement.style.opacity = opacity; 27. The DOM is a Mess Many cross browser issuesvar el = document.createElement("div"); document.body.appendChild(el);Small mistakes can degrade el.style.position = absolute; // more style changes performance 28. The DOM is a Mess Many cross browser issuesvar el = document.createElement("div"); document.body.appendChild(el);Small mistakes can degrade el.style.position = absolute; // more style changes performancevar el = document.createElement("div"); el.style.position = absolute; // more style changes document.body.appendChild(el); 29. The DOM is a Mess Many cross browser issues Small mistakes can degrade performance var width = parseInt(el.style.width); var height = parseInt(el.style.height);Reading styles/attributes is slow 30. The DOM is a Mess Many cross browser issues Small mistakes can degrade performance var width = parseInt(el.style.width); var height = parseInt(el.style.height);Reading styles/attributes is slow 31. DOM WrapperWrap DOM elements Lazy DOM element creation Batch DOM manipulations Cache styles/attributes 32. qx.Class.define("qx.ui.core.Widget", Container { extend : qx.core.Object,construct : function() { this.base(arguments); this._container = new qx.html.Element("div");this._content = new qx.html.Element("div").setStyles({ position: "absolute", zIndex: 10, overflow: "hidden" }); this._container.add(this._content);this._decorator = new qx.html.Element("div").setStyles({ position: "absolute", zIndex: 10 });this._container.add(this._decorator); } }); 33. qx.Class.define("qx.ui.core.Widget", Container { extend : qx.core.Object, Decorator construct : function() { this.base(arguments); this._container = new qx.html.Element("div");this._content = new qx.html.Element("div").setStyles({ position: "absolute", zIndex: 10, overflow: "hidden" }); this._container.add(this._content);this._decorator = new qx.html.Element("div").setStyles({ position: "absolute", zIndex: 10 });this._container.add(this._decorator); } }); 34. qx.Class.define("qx.ui.core.Widget", Container { extend : qx.core.Object, Decorator construct : function() Content { this.base(arguments); this._container = new qx.html.Element("div");this._content = new qx.html.Element("div").setStyles({ position: "absolute", zIndex: 10, overflow: "hidden" }); this._container.add(this._content);this._decorator = new qx.html.Element("div").setStyles({ position: "absolute", zIndex: 10 });this._container.add(this._decorator); } }); 35. DOM WrapperProCon Cross browser APIMemory overheadPerformanceAdditional complexity Keeps widget code clean 36. Dynamic Structure:Layouts 37. Initial Sizes 38. Initial Sizes 39. Initial Sizes 40. Dynamic Update 41. Layout Compute widgets sizes based onAvailable space Layout constraints Preferred widget sizes Widget size constraints 42. Algorithm 2 Passes 43. Algorithm2 Passes 1st Pass Compute Preferred Size 44. Algorithm2 Passes 1st Pass2nd Pass Compute Preferred SizeRender Size 45. 1st Pass Spinner Grid getSizeHint() getSizeHint() getLayout()getLayoutChildren()computeLayout(hints) TextField Button ButtongetSizeHint() getSizeHint()getSizeHint() measureTextHeight() getImageSize() getImageSize() 46. 1st Pass Spinner.getSizeHint = function() { Spinner Grid return this.getLayout().getSizeHint(); } getSizeHint()getSizeHint() getLayout() getLayoutChildren() computeLayout(hints) TextField Button ButtongetSizeHint() getSizeHint()getSizeHint() measureTextHeight() getImageSize() getImageSize() 47. 1st PassGrid.getSizeHint = function(){ Spinner Gridvar childrenHints = [];var children =getSizeHint() this.getLayoutChildren();getSizeHint()for (var i=0; i