View.hx 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. package hide.ui;
  2. enum DisplayPosition {
  3. Left;
  4. Center;
  5. Right;
  6. Bottom;
  7. }
  8. typedef ViewOptions = { ?position : DisplayPosition, ?width : Int }
  9. @:keepSub @:allow(hide.Ide)
  10. class View<T> extends hide.comp.Component {
  11. var container : golden.Container;
  12. var containerView : golden.ContentItem;
  13. var watches : Array<{ keep : Bool, path : String, callb : Void -> Void }> = [];
  14. public var fullScreen(get,set) : Bool;
  15. public var keys(get,null) : Keys;
  16. public var state(default, null) : T;
  17. public var undo(default, null) = new hide.ui.UndoHistory();
  18. public var config(get, null) : Config;
  19. public var viewClass(get, never) : String;
  20. public var defaultOptions(get,never) : ViewOptions;
  21. var contentWidth(get,never) : Int;
  22. var contentHeight(get,never) : Int;
  23. var needRebuild : Bool;
  24. public function new(state:T) {
  25. super(null,null);
  26. element = null;
  27. this.state = state;
  28. ide = Ide.inst;
  29. @:privateAccess ide.currentFullScreen = null;
  30. }
  31. public function watch( filePath : String, onChange : Void -> Void, ?opts : { ?checkDelete : Bool, ?keepOnRebuild : Bool } ) {
  32. if( opts == null ) opts = {};
  33. ide.fileWatcher.register(filePath, onChange, opts.checkDelete);
  34. var w = { keep : opts.keepOnRebuild, path : filePath, callb : onChange };
  35. watches.push(w);
  36. return w;
  37. }
  38. public function unwatch(w) {
  39. if( watches.remove(w) )
  40. ide.fileWatcher.unregister(w.path, w.callb);
  41. }
  42. function get_config() {
  43. if( config == null )
  44. config = ide.currentConfig;
  45. return config;
  46. }
  47. function get_keys() {
  48. if( keys == null ) {
  49. keys = new Keys(null);
  50. keys.register("view.fullScreen", function() fullScreen = !fullScreen);
  51. }
  52. return keys;
  53. }
  54. public function getTitle() {
  55. return viewClass.split(".").pop();
  56. }
  57. public function onBeforeClose() {
  58. return true;
  59. }
  60. function get_viewClass() {
  61. return Type.getClassName(Type.getClass(this));
  62. }
  63. public function setClipboard( v : Dynamic, ?type : String, ?opts : {} ) {
  64. nw.Clipboard.get().set(ide.toJSON({ type : type == null ? viewClass : type, value : v, opts : opts }));
  65. }
  66. public function hasClipboard( ?type : String ) {
  67. if( type == null ) type = viewClass;
  68. var v : Dynamic = try haxe.Json.parse(nw.Clipboard.get().get()) catch( e : Dynamic ) null;
  69. return v != null && v.type == type;
  70. }
  71. public function getClipboard( ?type : String, ?opts : { ref : Dynamic } ) : Dynamic {
  72. if( type == null ) type = viewClass;
  73. var v : Dynamic = try haxe.Json.parse(nw.Clipboard.get().get()) catch( e : Dynamic ) null;
  74. if( v != null && opts != null ) opts.ref = v.opts;
  75. return v == null || v.type != type ? null : v.value;
  76. }
  77. function syncTitle() {
  78. container.setTitle(getTitle());
  79. }
  80. function isKeysLocked( e : js.jquery.Event ) {
  81. var active = js.Browser.document.activeElement;
  82. if( active == null || e.altKey )
  83. return false;
  84. if( e.ctrlKey && e.keyCode != "A".code && e.keyCode != "C".code && e.keyCode != "V".code && e.keyCode != "X".code )
  85. return false;
  86. if( active.nodeName == "TEXTAREA" )
  87. return true;
  88. if( active.nodeName == "INPUT" ) {
  89. var type = (""+active.getAttribute("type")).toLowerCase();
  90. if( type == "text" || type == "null" || type == "number" || type == "password" || type == "date" )
  91. return true;
  92. }
  93. return false;
  94. }
  95. public function processKeyEvent( e : js.jquery.Event ) {
  96. if( isKeysLocked(e) ) {
  97. e.stopPropagation();
  98. return true;
  99. }
  100. var active = js.Browser.document.activeElement;
  101. for( el in element.find("[haskeys=true]").add(element).elements() ) {
  102. if(el.has(active).length == 0 && el[0] != active)
  103. continue;
  104. var keys = hide.ui.Keys.get(el);
  105. if( keys == null ) continue;
  106. if( keys.processEvent(e,config) )
  107. return true;
  108. }
  109. // global keys
  110. return keys.processEvent(e,config);
  111. }
  112. public function setContainer(cont) {
  113. this.container = cont;
  114. @:privateAccess ide.views.push(this);
  115. syncTitle();
  116. container.on("resize",function(_) {
  117. container.getElement().find('*').trigger('resize');
  118. onResize();
  119. });
  120. container.on("destroy",function(e) {
  121. destroy();
  122. });
  123. container.on("show", function(_) {
  124. // we are dragging the container tab
  125. if( container.parent.parent.config == null ) return;
  126. haxe.Timer.delay(onActivate,0);
  127. });
  128. container.getElement().keydown(function(e) {
  129. processKeyEvent(e);
  130. });
  131. container.on("tab", function(e) {
  132. container.tab.onClose = function() {
  133. return onBeforeClose();
  134. };
  135. container.tab.element.contextmenu(function(e) {
  136. var menu = buildTabMenu();
  137. if( menu.length > 0 ) new hide.comp.ContextMenu(menu);
  138. e.preventDefault();
  139. });
  140. });
  141. if( containerView != null ) containerView.__view = null;
  142. containerView = container.parent;
  143. containerView.__view = this;
  144. element = cont.getElement();
  145. }
  146. public final function rebuild() {
  147. function checkRebuild() {
  148. if( container == null || !needRebuild ) return;
  149. if( !isActive() ) {
  150. haxe.Timer.delay(checkRebuild,200);
  151. return;
  152. }
  153. needRebuild = false;
  154. onRebuild();
  155. }
  156. if( !needRebuild ) {
  157. needRebuild = true;
  158. checkRebuild();
  159. }
  160. }
  161. function onRebuild() {
  162. for( w in watches.copy() )
  163. if( !w.keep ) {
  164. ide.fileWatcher.unregister(w.path, w.callb);
  165. watches.remove(w);
  166. }
  167. syncTitle();
  168. element.empty();
  169. element.off();
  170. onDisplay();
  171. }
  172. function onDisplay() {
  173. element.text(viewClass+(state == null ? "" : " "+state));
  174. }
  175. public function onResize() {
  176. }
  177. public function onActivate() {
  178. }
  179. public function isActive() {
  180. return container != null && !container.isHidden;
  181. }
  182. public function onDragDrop(items : Array<String>, isDrop : Bool) {
  183. return false;
  184. }
  185. function toString() {
  186. return Type.getClassName(Type.getClass(this)) + (this.state == null ? "" : "("+haxe.Json.stringify(this.state)+")");
  187. }
  188. /**
  189. Gives focus if part of a tab group
  190. **/
  191. public function activate() {
  192. if( container != null ) {
  193. var cur = container.parent.parent.getActiveContentItem();
  194. if( cur != container.parent )
  195. container.parent.parent.setActiveContentItem(container.parent);
  196. }
  197. }
  198. function saveState() {
  199. container.setState(state);
  200. }
  201. public function close() {
  202. if( container != null )
  203. container.close();
  204. }
  205. function destroy() {
  206. for( w in watches.copy() )
  207. ide.fileWatcher.unregister(w.path, w.callb);
  208. watches = [];
  209. @:privateAccess ide.views.remove(this);
  210. for( c in container.getElement().find("canvas") ) {
  211. var s : hide.comp.Scene = Reflect.field(c, "__scene");
  212. if( s != null )
  213. s.dispose();
  214. }
  215. element = null;
  216. containerView.__view = null;
  217. }
  218. function buildTabMenu() : Array<hide.comp.ContextMenu.ContextMenuItem> {
  219. if( @:privateAccess ide.subView != null )
  220. return [];
  221. return [
  222. { label : "Close", click : close },
  223. { label : "Close Others", click : function() for( v in @:privateAccess ide.views.copy() ) if( v != this && v.container.tab.header == container.tab.header ) v.close() },
  224. { label : "Close All", click : function() for( v in @:privateAccess ide.views.copy() ) if( v.container.tab.header == container.tab.header ) v.close() },
  225. ];
  226. }
  227. function get_contentWidth() return container.width;
  228. function get_contentHeight() return container.height;
  229. function get_defaultOptions() return viewClasses.get(Type.getClassName(Type.getClass(this))).options;
  230. function get_fullScreen() return container != null && container.getElement().is(".fullScreen");
  231. function set_fullScreen(v) {
  232. if( fullScreen == v )
  233. return v;
  234. if( container != null ) {
  235. new Element(".fullScreen").removeClass("fullScreen");
  236. container.getElement().toggleClass("fullScreen", v);
  237. new Element("body").toggleClass("fullScreenMode",v);
  238. }
  239. @:privateAccess if( v ) ide.currentFullScreen = this else ide.currentFullScreen = null;
  240. if( !ide.isCDB ) ide.setFullscreen(v);
  241. return v;
  242. }
  243. public static var viewClasses = new Map<String,{ name : String, cl : Class<View<Dynamic>>, options : ViewOptions }>();
  244. public static function register<T>( cl : Class<View<T>>, ?options : ViewOptions ) {
  245. var name = Type.getClassName(cl);
  246. if( viewClasses.exists(name) )
  247. return null;
  248. if( options == null )
  249. options = {}
  250. if( options.position == null )
  251. options.position = Center;
  252. viewClasses.set(name, { name : name, cl : cl, options : options });
  253. return null;
  254. }
  255. }