Update:
I'll restate the question in hopefully more terse and clearer terms.
To illustrate the issue, run this and save the resulting notebook:
CreateDocument[
DynamicModule[
{ ultimateAnswer = 42,
x = False},
Pane@Row@{
Checkbox[Dynamic[x]], " Notebook modified ",Checkbox[Dynamic[Refresh["ModifiedInMemory" /. NotebookInformation[], UpdateInterval -> 0.1]]]}]
, WindowSize -> {320, 80}, WindowElements -> {}, WindowToolbars -> {}, WindowFrameElements -> {"CloseBox", "DocumentIcon", "ResizeArea"}];
Add a cell and close the notebook:
Reopen it, play a few with the checkbox:
Lastly, be sure the checkbox is on, and close the notebook.
Note that there's no external or internal clue about the notebook saved state. What will the Front End do? What should it do? Shouldn't the checkbox remain checked when you re-open the notebook?
Primary question: Should the Front End close the window without any warning the notebook has been modified?
Note that in this contrived example, the flag var is there to illustrate the issue, but perfectly could also be another near it with the result of a 7.2 billion years computation.
Secondary-- nice-to-have,-- question: in case this issue bothers you, how would you resolve the problem in a nice, elegant, concise Mathematica way?
Bonus question: you classify this as
- you're-very-very-picky-and-a-little-paranoid, irrelevant
- very minor
- mild curiosity
- serious
Now seriously, I've actually been bitten by this, and have spent significant time looking for a solution. If you attempt any involved GUI work, it's probable you won't notice this during development because the notebook morphs a lot. Only at the end, you clean up nice to discover all your persistent changes aren't being saved. You must then notify users that they have to save even though the window says nothing has changed.
Old post:
The documentation of DynamicModule
states:
Symbols specified in a DynamicModule will by default have their values maintained even across Mathematica sessions.
So, any change in DynamicValues
vars able to persist should be reflected somehow. In particular, notebook saved state should be modified in this case, as if the user had created a new cell.
As a contrived example, evaluate this:
CreateDocument[
DynamicModule[{x = False},
Pane@Row@{
Checkbox[Dynamic[x]],
" Notebook modified ",
Checkbox[
Dynamic[Refresh["ModifiedInMemory" /. NotebookInformation[], UpdateInterval -> 0.1]]]}]
, WindowSize -> {230, 80}, WindowElements -> {}, WindowToolbars -> {},
WindowFrameElements -> {"CloseBox", "DocumentIcon", "ResizeArea"}];
Observe the resulting notebook, in particular the close button and document icon. The first Checkbox
is backed by a DynamicModule
var, second one monitors if there're unsaved changes.
Adding a new cell (1st-2nd images) triggers the normal UI clues of unsaved data: in OSX, the filled close button and the grayed-out document icon.
Changing the checkbox (3rd-4th images), though, doesn't change the UI nor, more importantly, the internal document state. In fact, you can close the document without FE prompting to save it and observe subsequently that the first checkbox doesn't remember its setting. If you want UI persistence, you must force the saving. Other widgets behave similarly. The culprit here is surely DynamicModule
, not the widgets or Dynamic
.
This is probably well known, and I see two explanations here. One, a bug/glitch (tested on Mathematica 9.0.1/OSX 10.9.2); two, this behavior is by design or tough to implement by the FE. Note that window resizing or zoom doesn't modify the notebook saved state also, even though the document metadata has changed.
My take is the second case seems somewhat inconsistent from a UX point of view. The old controversy of data vs metadata remains unresolved and impractical, but in my opinion DynamicModule
vars should be considered full data citizens. DynamicModule
raison d'être is session persistence. Some the strong points of Mathematica are the homoiconic, literate programming aspects, the unholy mix of code/data/metadata/application/IDE/document, and arguably DynamicModule
vars are so data as any user-entered expression. If you make a Cell/Show Expression, it's easily observable that the DynamicModule
vars are being modified and do change the cell expression:
Cell[BoxData[
DynamicModuleBox[{$\
CellContext`x$$ = True},
PaneBox[
TemplateBox[{CheckboxBox[
...
Whether it's a bug or designed behaviour, I believe WRI won't change this in the near future. But I like my notebooks reflecting DynamicModule
state changes. As a newbie, I see two quick'n'dirty very unsatisfactory solutions:
Track the vars and save programmatically the document,
Checkbox[Dynamic[x,(x=#;FrontEndTokenExecute["Save"])&]]
or
Checkbox[Dynamic[x,(x=#;NotebookSave[])&]]
But, it's the user who should choose how and when to save the document, not the application.
Create/delete ghost content, e.g.,
With[{nb = EvaluationNotebook[], scr = AutoScroll -> False}, SelectionMove[nb, After, Cell, scr]; NotebookWrite[nb, Cell["", "Text"], All, scr]; NotebookDelete[nb, scr]];
Ugly, and ungainly, and error-prone, not only for the fake changes, but for the selection resetting. I haven't found a way to fully restore the selection state (and this is something the FE does well: dynamic interactivity usually respects the selection point.)
My question, then: is there a more elegant (concise, convenient) way to mark the notebook as changed so both UI and internal notebook state reflect the fact?
Checkbox[Dynamic@x]
andCheckbox[x]
which tells us that it's not aboutDynamicModuleVariables
butDynamic
cells in general. $\endgroup$Checkbox
with save option will be useless if you changex
somewhere else. You way is only a response to interactive change via checkbox. More general aproach would be to add some up values withx
andSet
but stil ugly. $\endgroup$