index.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. 'use strict';
  2. var path = require('path');
  3. var electron = require('electron');
  4. var jsonfile = require('jsonfile');
  5. var mkdirp = require('mkdirp');
  6. var deepEqual = require('deep-equal');
  7. module.exports = function (options) {
  8. var app = electron.app || electron.remote.app;
  9. var screen = electron.screen || electron.remote.screen;
  10. var state;
  11. var winRef;
  12. var stateChangeTimer;
  13. var eventHandlingDelay = 100;
  14. var config = Object.assign({
  15. file: 'window-state.json',
  16. path: app.getPath('userData'),
  17. maximize: true,
  18. fullScreen: true
  19. }, options);
  20. var fullStoreFileName = path.join(config.path, config.file);
  21. function isNormal(win) {
  22. return !win.isMaximized() && !win.isMinimized() && !win.isFullScreen();
  23. }
  24. function hasBounds() {
  25. return state &&
  26. Number.isInteger(state.x) &&
  27. Number.isInteger(state.y) &&
  28. Number.isInteger(state.width) && state.width > 0 &&
  29. Number.isInteger(state.height) && state.height > 0;
  30. }
  31. function validateState() {
  32. var isValid = state && (hasBounds() || state.isMaximized || state.isFullScreen);
  33. if (!isValid) {
  34. state = null;
  35. return;
  36. }
  37. if (hasBounds() && state.displayBounds) {
  38. // Check if the display where the window was last open is still available
  39. var displayBounds = screen.getDisplayMatching(state).bounds;
  40. var sameBounds = deepEqual(state.displayBounds, displayBounds, {strict: true});
  41. if (!sameBounds) {
  42. if (displayBounds.width < state.displayBounds.width) {
  43. if (state.x > displayBounds.width) {
  44. state.x = 0;
  45. }
  46. if (state.width > displayBounds.width) {
  47. state.width = displayBounds.width;
  48. }
  49. }
  50. if (displayBounds.height < state.displayBounds.height) {
  51. if (state.y > displayBounds.height) {
  52. state.y = 0;
  53. }
  54. if (state.height > displayBounds.height) {
  55. state.height = displayBounds.height;
  56. }
  57. }
  58. }
  59. }
  60. }
  61. function updateState(win) {
  62. win = win || winRef;
  63. if (!win) {
  64. return;
  65. }
  66. // don't throw an error when window was closed
  67. try {
  68. var winBounds = win.getBounds();
  69. if (isNormal(win)) {
  70. state.x = winBounds.x;
  71. state.y = winBounds.y;
  72. state.width = winBounds.width;
  73. state.height = winBounds.height;
  74. }
  75. state.isMaximized = win.isMaximized();
  76. state.isFullScreen = win.isFullScreen();
  77. state.displayBounds = screen.getDisplayMatching(winBounds).bounds;
  78. } catch (err) {}
  79. }
  80. function saveState(win) {
  81. // Update window state only if it was provided
  82. if (win) {
  83. updateState(win);
  84. }
  85. // Save state
  86. try {
  87. mkdirp.sync(path.dirname(fullStoreFileName));
  88. jsonfile.writeFileSync(fullStoreFileName, state);
  89. } catch (err) {
  90. // Don't care
  91. }
  92. }
  93. function stateChangeHandler() {
  94. // Handles both 'resize' and 'move'
  95. clearTimeout(stateChangeTimer);
  96. stateChangeTimer = setTimeout(updateState, eventHandlingDelay);
  97. }
  98. function closeHandler() {
  99. updateState();
  100. }
  101. function closedHandler() {
  102. // Unregister listeners and save state
  103. unmanage();
  104. saveState();
  105. }
  106. function manage(win) {
  107. if (config.maximize && state.isMaximized) {
  108. win.maximize();
  109. }
  110. if (config.fullScreen && state.isFullScreen) {
  111. win.setFullScreen(true);
  112. }
  113. win.on('resize', stateChangeHandler);
  114. win.on('move', stateChangeHandler);
  115. win.on('close', closeHandler);
  116. win.on('closed', closedHandler);
  117. winRef = win;
  118. }
  119. function unmanage() {
  120. if (winRef) {
  121. winRef.removeListener('resize', stateChangeHandler);
  122. winRef.removeListener('move', stateChangeHandler);
  123. clearTimeout(stateChangeTimer);
  124. winRef.removeListener('close', closeHandler);
  125. winRef.removeListener('closed', closedHandler);
  126. winRef = null;
  127. }
  128. }
  129. // Load previous state
  130. try {
  131. state = jsonfile.readFileSync(fullStoreFileName);
  132. } catch (err) {
  133. // Don't care
  134. }
  135. // Check state validity
  136. validateState();
  137. // Set state fallback values
  138. state = Object.assign({
  139. width: config.defaultWidth || 800,
  140. height: config.defaultHeight || 600
  141. }, state);
  142. return {
  143. get x() { return state.x; },
  144. get y() { return state.y; },
  145. get width() { return state.width; },
  146. get height() { return state.height; },
  147. get isMaximized() { return state.isMaximized; },
  148. get isFullScreen() { return state.isFullScreen; },
  149. saveState: saveState,
  150. unmanage: unmanage,
  151. manage: manage
  152. };
  153. };