Fossil SCM

Added style_select_list_str(), selection list for commit comment mime type, and a toggle to switch between single- and multi-line comment editing modes.

stephan 2020-05-09 12:36 fileedit-ajaxify
Commit 4d5004ef2d8cdf02bd106910d18f76eeb7b8b979bfd9d3f88fbb73c13e275449
--- src/default_css.txt
+++ src/default_css.txt
@@ -996,11 +996,11 @@
996996
#fileedit-comment {
997997
width: 100%;
998998
font-family: monospace;
999999
}
10001000
.tab-container > .tabs > .tab-panel > .fileedit-options {
1001
- margin-top: 0;
1001
+ margin-top: 0.25em 0;
10021002
border: none;
10031003
border-radius: 0;
10041004
border-bottom-width: 1px;
10051005
border-bottom-style: dotted;
10061006
}
@@ -1017,10 +1017,13 @@
10171017
.flex-container.row {
10181018
flex-direction: row;
10191019
flex-wrap: wrap;
10201020
justify-content: center;
10211021
align-items: center;
1022
+}
1023
+.fileedit-options.flex-container.row {
1024
+ align-items: first baseline;
10221025
}
10231026
.flex-container.row.stretch {
10241027
flex-direction: row;
10251028
flex-wrap: wrap;
10261029
align-items: stretch;
10271030
--- src/default_css.txt
+++ src/default_css.txt
@@ -996,11 +996,11 @@
996 #fileedit-comment {
997 width: 100%;
998 font-family: monospace;
999 }
1000 .tab-container > .tabs > .tab-panel > .fileedit-options {
1001 margin-top: 0;
1002 border: none;
1003 border-radius: 0;
1004 border-bottom-width: 1px;
1005 border-bottom-style: dotted;
1006 }
@@ -1017,10 +1017,13 @@
1017 .flex-container.row {
1018 flex-direction: row;
1019 flex-wrap: wrap;
1020 justify-content: center;
1021 align-items: center;
 
 
 
1022 }
1023 .flex-container.row.stretch {
1024 flex-direction: row;
1025 flex-wrap: wrap;
1026 align-items: stretch;
1027
--- src/default_css.txt
+++ src/default_css.txt
@@ -996,11 +996,11 @@
996 #fileedit-comment {
997 width: 100%;
998 font-family: monospace;
999 }
1000 .tab-container > .tabs > .tab-panel > .fileedit-options {
1001 margin-top: 0.25em 0;
1002 border: none;
1003 border-radius: 0;
1004 border-bottom-width: 1px;
1005 border-bottom-style: dotted;
1006 }
@@ -1017,10 +1017,13 @@
1017 .flex-container.row {
1018 flex-direction: row;
1019 flex-wrap: wrap;
1020 justify-content: center;
1021 align-items: center;
1022 }
1023 .fileedit-options.flex-container.row {
1024 align-items: first baseline;
1025 }
1026 .flex-container.row.stretch {
1027 flex-direction: row;
1028 flex-wrap: wrap;
1029 align-items: stretch;
1030
+29 -12
--- src/fileedit.c
+++ src/fileedit.c
@@ -1788,23 +1788,40 @@
17881788
CX("</div>"/*checkboxes*/);
17891789
}
17901790
17911791
{ /******* Commit comment, button, and result manifest *******/
17921792
CX("<fieldset class='fileedit-options'>"
1793
- "<legend>Message (required)</legend><div>");
1793
+ "<legend>Message (required) "
1794
+ "</legend><div>");
1795
+ /* We have two comment input fields, defaulting to single-line
1796
+ ** mode. JS code sets up the ability to toggle between single-
1797
+ ** and multi-line modes. */
17941798
CX("<input type='text' name='comment' "
1795
- "id='fileedit-comment'>");
1796
- /* ^^^ adding the 'required' attribute means we cannot even
1797
- submit for PREVIEW mode if it's empty :/. */
1798
- if(blob_size(&cimi.comment)){
1799
- blob_appendf(&endScript,
1800
- "document.querySelector('#fileedit-comment').value="
1801
- "\"%h\";\n", blob_str(&cimi.comment));
1802
- }
1803
- CX("</input>\n");
1804
- CX("<div class='fileedit-hint'>Comments use the Fossil wiki markup "
1805
- "syntax.</div>\n"/*TODO: select for fossil/md/plain text*/);
1799
+ "id='fileedit-comment'></input>\n");
1800
+ CX("<textarea name='commentBig' class='hidden' "
1801
+ "rows='5' id='fileedit-comment-big'></textarea>");
1802
+ { /* comment options... */
1803
+ CX("<div class='fileedit-options flex-container row'>");
1804
+ CX("<button id='comment-toggle' "
1805
+ "title='Toggle between single- and multi-line comment mode, "
1806
+ "noting that switching from multi- to single-line may cause "
1807
+ "newlines to get stripped.'"
1808
+ ">toggle single-/multi-line</button> ");
1809
+ style_select_list_str("comment-mimetype", "comment_mimetype",
1810
+ "Comment style:",
1811
+ "Specify how fossil will interpret the "
1812
+ "comment string.",
1813
+ NULL,
1814
+ "Fossil", "text/x-fossil-wiki",
1815
+ "Markdown", "text/x-markdown",
1816
+ "Plain text", "text/plain",
1817
+ NULL);
1818
+ CX("</div>\n");
1819
+ CX("<div class='fileedit-hint flex-container row'>"
1820
+ "(Warning: switching from multi- to single-line mode will "
1821
+ "strip out all newlines!)</div>");
1822
+ }
18061823
CX("</div></fieldset>\n"/*commit comment*/);
18071824
CX("<div class='flex-container row'>"
18081825
"<button id='fileedit-btn-commit'>Commit</button>"
18091826
"</div>\n");
18101827
CX("<div id='fileedit-manifest'></div>\n");
18111828
--- src/fileedit.c
+++ src/fileedit.c
@@ -1788,23 +1788,40 @@
1788 CX("</div>"/*checkboxes*/);
1789 }
1790
1791 { /******* Commit comment, button, and result manifest *******/
1792 CX("<fieldset class='fileedit-options'>"
1793 "<legend>Message (required)</legend><div>");
 
 
 
 
1794 CX("<input type='text' name='comment' "
1795 "id='fileedit-comment'>");
1796 /* ^^^ adding the 'required' attribute means we cannot even
1797 submit for PREVIEW mode if it's empty :/. */
1798 if(blob_size(&cimi.comment)){
1799 blob_appendf(&endScript,
1800 "document.querySelector('#fileedit-comment').value="
1801 "\"%h\";\n", blob_str(&cimi.comment));
1802 }
1803 CX("</input>\n");
1804 CX("<div class='fileedit-hint'>Comments use the Fossil wiki markup "
1805 "syntax.</div>\n"/*TODO: select for fossil/md/plain text*/);
 
 
 
 
 
 
 
 
 
 
 
 
 
1806 CX("</div></fieldset>\n"/*commit comment*/);
1807 CX("<div class='flex-container row'>"
1808 "<button id='fileedit-btn-commit'>Commit</button>"
1809 "</div>\n");
1810 CX("<div id='fileedit-manifest'></div>\n");
1811
--- src/fileedit.c
+++ src/fileedit.c
@@ -1788,23 +1788,40 @@
1788 CX("</div>"/*checkboxes*/);
1789 }
1790
1791 { /******* Commit comment, button, and result manifest *******/
1792 CX("<fieldset class='fileedit-options'>"
1793 "<legend>Message (required) "
1794 "</legend><div>");
1795 /* We have two comment input fields, defaulting to single-line
1796 ** mode. JS code sets up the ability to toggle between single-
1797 ** and multi-line modes. */
1798 CX("<input type='text' name='comment' "
1799 "id='fileedit-comment'></input>\n");
1800 CX("<textarea name='commentBig' class='hidden' "
1801 "rows='5' id='fileedit-comment-big'></textarea>");
1802 { /* comment options... */
1803 CX("<div class='fileedit-options flex-container row'>");
1804 CX("<button id='comment-toggle' "
1805 "title='Toggle between single- and multi-line comment mode, "
1806 "noting that switching from multi- to single-line may cause "
1807 "newlines to get stripped.'"
1808 ">toggle single-/multi-line</button> ");
1809 style_select_list_str("comment-mimetype", "comment_mimetype",
1810 "Comment style:",
1811 "Specify how fossil will interpret the "
1812 "comment string.",
1813 NULL,
1814 "Fossil", "text/x-fossil-wiki",
1815 "Markdown", "text/x-markdown",
1816 "Plain text", "text/plain",
1817 NULL);
1818 CX("</div>\n");
1819 CX("<div class='fileedit-hint flex-container row'>"
1820 "(Warning: switching from multi- to single-line mode will "
1821 "strip out all newlines!)</div>");
1822 }
1823 CX("</div></fieldset>\n"/*commit comment*/);
1824 CX("<div class='flex-container row'>"
1825 "<button id='fileedit-btn-commit'>Commit</button>"
1826 "</div>\n");
1827 CX("<div id='fileedit-manifest'></div>\n");
1828
--- src/fossil.page.fileedit.js
+++ src/fossil.page.fileedit.js
@@ -9,11 +9,12 @@
99
P = F.page;
1010
window.addEventListener("load", function() {
1111
P.tabs = new fossil.TabManager('#fileedit-tabs');
1212
P.e = {
1313
taEditor: E('#fileedit-content-editor'),
14
- taComment: E('#fileedit-comment'),
14
+ taCommentSmall: E('#fileedit-comment'),
15
+ taCommentBig: E('#fileedit-comment-big'),
1516
ajaxContentTarget: E('#ajax-target'),
1617
btnCommit: E("#fileedit-btn-commit"),
1718
btnReload: E("#fileedit-tab-content > .fileedit-options > "
1819
+"button.fileedit-content-reload"),
1920
selectPreviewModeWrap: E('#select-preview-mode'),
@@ -25,10 +26,11 @@
2526
preview: E('#fileedit-tab-preview'),
2627
diff: E('#fileedit-tab-diff'),
2728
commit: E('#fileedit-tab-commit')
2829
}
2930
};
31
+ P.e.taComment = P.e.taCommentSmall;
3032
3133
P.tabs.e.container.insertBefore(
3234
E('#fossil-status-bar'), P.tabs.e.tabs
3335
);
3436
@@ -58,10 +60,14 @@
5860
F.confirmer(P.e.btnReload, {
5961
confirmText: "Really reload, losing edits?",
6062
onconfirm: (e)=>P.loadFile(),
6163
ticks: 3
6264
});
65
+ E('#comment-toggle').addEventListener(
66
+ "click",(e)=>P.toggleCommentMode(), false
67
+ );
68
+
6369
/**
6470
Cosmetic: jump through some hoops to enable/disable
6571
certain preview options depending on the current
6672
preview mode...
6773
*/
@@ -103,10 +109,41 @@
103109
// Force UI update
104110
new Event('change',{target:selectFontSize})
105111
);
106112
}
107113
}, false)/*onload event handler*/;
114
+
115
+ /**
116
+ Toggles between single- and multi-line comment
117
+ mode.
118
+ */
119
+ P.toggleCommentMode = function(){
120
+ var s, h, c = this.e.taComment.value;
121
+ if(this.e.taComment === this.e.taCommentSmall){
122
+ s = this.e.taCommentBig;
123
+ h = this.e.taCommentSmall;
124
+ }else{
125
+ s = this.e.taCommentSmall;
126
+ h = this.e.taCommentBig;
127
+ /*
128
+ Doing (input[type=text].value = textarea.value) unfortunately
129
+ strips all newlines. To compensate we'll replace each EOL with
130
+ a space. Not ideal. If we were to instead escape them as \n,
131
+ and do the reverse when toggling again, then they would get
132
+ committed as escaped newlines if the user did not first switch
133
+ back to multi-line mode. We cannot blindly unescape the
134
+ newlines, in the off chance that the user actually enters \n
135
+ in the comment.
136
+ */
137
+ c = c.replace(/\r?\n/g,' ');
138
+ }
139
+ s.value = c;
140
+ this.e.taComment = s;
141
+ D.addClass(h, 'hidden');
142
+ D.removeClass(s, 'hidden');
143
+ console.debug(s,h);
144
+ };
108145
109146
/**
110147
updateVersion() updates the filename and version in various UI
111148
elements...
112149
113150
--- src/fossil.page.fileedit.js
+++ src/fossil.page.fileedit.js
@@ -9,11 +9,12 @@
9 P = F.page;
10 window.addEventListener("load", function() {
11 P.tabs = new fossil.TabManager('#fileedit-tabs');
12 P.e = {
13 taEditor: E('#fileedit-content-editor'),
14 taComment: E('#fileedit-comment'),
 
15 ajaxContentTarget: E('#ajax-target'),
16 btnCommit: E("#fileedit-btn-commit"),
17 btnReload: E("#fileedit-tab-content > .fileedit-options > "
18 +"button.fileedit-content-reload"),
19 selectPreviewModeWrap: E('#select-preview-mode'),
@@ -25,10 +26,11 @@
25 preview: E('#fileedit-tab-preview'),
26 diff: E('#fileedit-tab-diff'),
27 commit: E('#fileedit-tab-commit')
28 }
29 };
 
30
31 P.tabs.e.container.insertBefore(
32 E('#fossil-status-bar'), P.tabs.e.tabs
33 );
34
@@ -58,10 +60,14 @@
58 F.confirmer(P.e.btnReload, {
59 confirmText: "Really reload, losing edits?",
60 onconfirm: (e)=>P.loadFile(),
61 ticks: 3
62 });
 
 
 
 
63 /**
64 Cosmetic: jump through some hoops to enable/disable
65 certain preview options depending on the current
66 preview mode...
67 */
@@ -103,10 +109,41 @@
103 // Force UI update
104 new Event('change',{target:selectFontSize})
105 );
106 }
107 }, false)/*onload event handler*/;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
109 /**
110 updateVersion() updates the filename and version in various UI
111 elements...
112
113
--- src/fossil.page.fileedit.js
+++ src/fossil.page.fileedit.js
@@ -9,11 +9,12 @@
9 P = F.page;
10 window.addEventListener("load", function() {
11 P.tabs = new fossil.TabManager('#fileedit-tabs');
12 P.e = {
13 taEditor: E('#fileedit-content-editor'),
14 taCommentSmall: E('#fileedit-comment'),
15 taCommentBig: E('#fileedit-comment-big'),
16 ajaxContentTarget: E('#ajax-target'),
17 btnCommit: E("#fileedit-btn-commit"),
18 btnReload: E("#fileedit-tab-content > .fileedit-options > "
19 +"button.fileedit-content-reload"),
20 selectPreviewModeWrap: E('#select-preview-mode'),
@@ -25,10 +26,11 @@
26 preview: E('#fileedit-tab-preview'),
27 diff: E('#fileedit-tab-diff'),
28 commit: E('#fileedit-tab-commit')
29 }
30 };
31 P.e.taComment = P.e.taCommentSmall;
32
33 P.tabs.e.container.insertBefore(
34 E('#fossil-status-bar'), P.tabs.e.tabs
35 );
36
@@ -58,10 +60,14 @@
60 F.confirmer(P.e.btnReload, {
61 confirmText: "Really reload, losing edits?",
62 onconfirm: (e)=>P.loadFile(),
63 ticks: 3
64 });
65 E('#comment-toggle').addEventListener(
66 "click",(e)=>P.toggleCommentMode(), false
67 );
68
69 /**
70 Cosmetic: jump through some hoops to enable/disable
71 certain preview options depending on the current
72 preview mode...
73 */
@@ -103,10 +109,41 @@
109 // Force UI update
110 new Event('change',{target:selectFontSize})
111 );
112 }
113 }, false)/*onload event handler*/;
114
115 /**
116 Toggles between single- and multi-line comment
117 mode.
118 */
119 P.toggleCommentMode = function(){
120 var s, h, c = this.e.taComment.value;
121 if(this.e.taComment === this.e.taCommentSmall){
122 s = this.e.taCommentBig;
123 h = this.e.taCommentSmall;
124 }else{
125 s = this.e.taCommentSmall;
126 h = this.e.taCommentBig;
127 /*
128 Doing (input[type=text].value = textarea.value) unfortunately
129 strips all newlines. To compensate we'll replace each EOL with
130 a space. Not ideal. If we were to instead escape them as \n,
131 and do the reverse when toggling again, then they would get
132 committed as escaped newlines if the user did not first switch
133 back to multi-line mode. We cannot blindly unescape the
134 newlines, in the off chance that the user actually enters \n
135 in the comment.
136 */
137 c = c.replace(/\r?\n/g,' ');
138 }
139 s.value = c;
140 this.e.taComment = s;
141 D.addClass(h, 'hidden');
142 D.removeClass(s, 'hidden');
143 console.debug(s,h);
144 };
145
146 /**
147 updateVersion() updates the filename and version in various UI
148 elements...
149
150
+62 -1
--- src/style.c
+++ src/style.c
@@ -1416,15 +1416,76 @@
14161416
CX("%d",v);
14171417
}
14181418
CX("</option>\n");
14191419
}
14201420
CX("</select>\n");
1421
+ CX("</span>\n");
1422
+ va_end(vargs);
1423
+}
1424
+
1425
+/*
1426
+** The C-string counterpart of style_select_list_int(), this variant
1427
+** differs only in that its variadic arguments are C-strings in pairs
1428
+** of (optionLabel, optionValue). If a given optionLabel is an empty
1429
+** string, the corresponding optionValue is used as its label. If any
1430
+** given value matches zSelectedVal, that option gets preselected. If
1431
+** no options match zSelectedVal then the first entry is selected by
1432
+** default.
1433
+**
1434
+** Any of (zWrapperId, zTooltip, zSelectedVal) may be NULL or empty.
1435
+**
1436
+** Example:
1437
+**
1438
+** style_select_list_str("my-grapes", "my_grapes", "Grapes",
1439
+** "Select the number of grapes",
1440
+** P("my_field"),
1441
+** "1", "One", "2", "Two", "", "3",
1442
+** NULL);
1443
+*/
1444
+void style_select_list_str(const char * zWrapperId,
1445
+ const char *zFieldName, const char * zLabel,
1446
+ const char * zToolTip, char const * zSelectedVal,
1447
+ ... ){
1448
+ va_list vargs;
1449
+
1450
+ va_start(vargs,zSelectedVal);
1451
+ if(!zSelectedVal){
1452
+ zSelectedVal = __FILE__/*some string we'll never match*/;
1453
+ }
1454
+ CX("<span class='input-with-label'");
1455
+ if(zToolTip && *zToolTip){
1456
+ CX(" title='%h'",zToolTip);
1457
+ }
1458
+ if(zWrapperId && *zWrapperId){
1459
+ CX(" id='%s'",zWrapperId);
1460
+ }
1461
+ CX(">");
14211462
if(zLabel && *zLabel){
1422
- CX("</span>\n");
1463
+ CX("<span>%h</span>", zLabel);
1464
+ }
1465
+ CX("<select name='%s'>",zFieldName);
1466
+ while(1){
1467
+ const char * zLabel = va_arg(vargs,char *);
1468
+ const char * zVal;
1469
+ if(NULL==zLabel){
1470
+ break;
1471
+ }
1472
+ zVal = va_arg(vargs,char *);
1473
+ CX("<option value='%T'%s>",
1474
+ zVal, 0==fossil_strcmp(zVal, zSelectedVal) ? " selected" : "");
1475
+ if(*zLabel){
1476
+ CX("%s", zLabel);
1477
+ }else{
1478
+ CX("%h",zVal);
1479
+ }
1480
+ CX("</option>\n");
14231481
}
1482
+ CX("</select>\n");
1483
+ CX("</span>\n");
14241484
va_end(vargs);
14251485
}
1486
+
14261487
14271488
/*
14281489
** The first time this is called, it emits code to install and
14291490
** bootstrap the window.fossil object, using the built-in file
14301491
** fossil.bootstrap.js (not to be confused with bootstrap.js).
14311492
--- src/style.c
+++ src/style.c
@@ -1416,15 +1416,76 @@
1416 CX("%d",v);
1417 }
1418 CX("</option>\n");
1419 }
1420 CX("</select>\n");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1421 if(zLabel && *zLabel){
1422 CX("</span>\n");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1423 }
 
 
1424 va_end(vargs);
1425 }
 
1426
1427 /*
1428 ** The first time this is called, it emits code to install and
1429 ** bootstrap the window.fossil object, using the built-in file
1430 ** fossil.bootstrap.js (not to be confused with bootstrap.js).
1431
--- src/style.c
+++ src/style.c
@@ -1416,15 +1416,76 @@
1416 CX("%d",v);
1417 }
1418 CX("</option>\n");
1419 }
1420 CX("</select>\n");
1421 CX("</span>\n");
1422 va_end(vargs);
1423 }
1424
1425 /*
1426 ** The C-string counterpart of style_select_list_int(), this variant
1427 ** differs only in that its variadic arguments are C-strings in pairs
1428 ** of (optionLabel, optionValue). If a given optionLabel is an empty
1429 ** string, the corresponding optionValue is used as its label. If any
1430 ** given value matches zSelectedVal, that option gets preselected. If
1431 ** no options match zSelectedVal then the first entry is selected by
1432 ** default.
1433 **
1434 ** Any of (zWrapperId, zTooltip, zSelectedVal) may be NULL or empty.
1435 **
1436 ** Example:
1437 **
1438 ** style_select_list_str("my-grapes", "my_grapes", "Grapes",
1439 ** "Select the number of grapes",
1440 ** P("my_field"),
1441 ** "1", "One", "2", "Two", "", "3",
1442 ** NULL);
1443 */
1444 void style_select_list_str(const char * zWrapperId,
1445 const char *zFieldName, const char * zLabel,
1446 const char * zToolTip, char const * zSelectedVal,
1447 ... ){
1448 va_list vargs;
1449
1450 va_start(vargs,zSelectedVal);
1451 if(!zSelectedVal){
1452 zSelectedVal = __FILE__/*some string we'll never match*/;
1453 }
1454 CX("<span class='input-with-label'");
1455 if(zToolTip && *zToolTip){
1456 CX(" title='%h'",zToolTip);
1457 }
1458 if(zWrapperId && *zWrapperId){
1459 CX(" id='%s'",zWrapperId);
1460 }
1461 CX(">");
1462 if(zLabel && *zLabel){
1463 CX("<span>%h</span>", zLabel);
1464 }
1465 CX("<select name='%s'>",zFieldName);
1466 while(1){
1467 const char * zLabel = va_arg(vargs,char *);
1468 const char * zVal;
1469 if(NULL==zLabel){
1470 break;
1471 }
1472 zVal = va_arg(vargs,char *);
1473 CX("<option value='%T'%s>",
1474 zVal, 0==fossil_strcmp(zVal, zSelectedVal) ? " selected" : "");
1475 if(*zLabel){
1476 CX("%s", zLabel);
1477 }else{
1478 CX("%h",zVal);
1479 }
1480 CX("</option>\n");
1481 }
1482 CX("</select>\n");
1483 CX("</span>\n");
1484 va_end(vargs);
1485 }
1486
1487
1488 /*
1489 ** The first time this is called, it emits code to install and
1490 ** bootstrap the window.fossil object, using the built-in file
1491 ** fossil.bootstrap.js (not to be confused with bootstrap.js).
1492

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button