Game Maker GML Flood Fill Method - at a dead end - queue

I'm trying to implement a flood fill script in Game Maker's GML and have hit a dead end. After trying for 3 hours to fix what I thought would be a simple solution to filling in floor blocks, I'm begging for help on this issue. I'm testing for whether a tile has already been added to the tile list and whether or not it would collide with a wall (hence the 10-line collision_rectangle functions; those are just testing for whether a floor tile would overlap a wall or not). Sorry these take up a lot of unnecessary space and clunk up the code, I couldn't find another way to do it in GML. If anyone here is familiar with the data structures in GML and knows what I'm doing wrong, please speak up! You would save my project!
Code:
// 2 arrays: one with x values of each tile, one with y values of each
tile_locations_x = ds_list_create();
tile_locations_y = ds_list_create();
tile_stack_x = ds_stack_create();
tile_stack_y = ds_stack_create();
ds_stack_push(tile_stack_x, obj_player.x);
ds_stack_push(tile_stack_y, obj_player.y);
// iterator for "for" loops
var i;
while (!ds_stack_empty(tile_stack_x)) {
test_location_x = ds_stack_pop(tile_stack_x);
test_location_y = ds_stack_pop(tile_stack_y);
ds_list_add(tile_locations_x, test_location_x);
ds_list_add(tile_locations_y, test_location_y);
tile_add(bg_floortiles, 0, 0, 16, 16, test_location_x, test_location_y, 99999);
// Add the 4 cardinal directions to the stack after checking if they're already in the list
// left of point
testpoint_x = test_location_x - 16;
testpoint_y = test_location_y;
// Check point testpoint_x, testpoint_y is valid
valid = true;
// Check added location list first, then check stack because the same location stackd multiple times doesn't work
for (i = 0; i < ds_list_size(tile_locations_x); i += 1) {
if (ds_list_find_value(tile_locations_x, i) == testpoint_x && ds_list_find_value(tile_locations_y, i) == testpoint_y) {
valid = false;
break;
}
}
if (valid) {
precise_collision_check = true;
if ( collision_rectangle(testpoint_x, testpoint_y, testpoint_x + 16, testpoint_y + 16, obj_horizontal, precise_collision_check, true) == noone &&
collision_rectangle(testpoint_x, testpoint_y, testpoint_x + 16, testpoint_y + 16, obj_vertical, precise_collision_check, true) == noone &&
collision_rectangle(testpoint_x, testpoint_y, testpoint_x + 16, testpoint_y + 16, obj_diagonal1, precise_collision_check, true) == noone &&
collision_rectangle(testpoint_x, testpoint_y, testpoint_x + 16, testpoint_y + 16, obj_diagonal2, precise_collision_check, true) == noone &&
collision_rectangle(testpoint_x, testpoint_y, testpoint_x + 16, testpoint_y + 16, obj_diagonal3, precise_collision_check, true) == noone &&
collision_rectangle(testpoint_x, testpoint_y, testpoint_x + 16, testpoint_y + 16, obj_diagonal4, precise_collision_check, true) == noone &&
collision_rectangle(testpoint_x, testpoint_y, testpoint_x + 16, testpoint_y + 16, obj_diagonalconnector1, precise_collision_check, true) == noone &&
collision_rectangle(testpoint_x, testpoint_y, testpoint_x + 16, testpoint_y + 16, obj_diagonalconnector2, precise_collision_check, true) == noone &&
collision_rectangle(testpoint_x, testpoint_y, testpoint_x + 16, testpoint_y + 16, obj_diagonalconnector3, precise_collision_check, true) == noone &&
collision_rectangle(testpoint_x, testpoint_y, testpoint_x + 16, testpoint_y + 16, obj_diagonalconnector4, precise_collision_check, true) == noone) {
ds_stack_push(tile_stack_x, testpoint_x);
ds_stack_push(tile_stack_y, testpoint_y);
}
}
// right of point
testpoint_x = test_location_x + 16;
testpoint_y = test_location_y;
// Check point testpoint_x, testpoint_y is valid
valid = true;
// Check added location list first, then check stack because the same location stackd multiple times doesn't work
for (i = 0; i < ds_list_size(tile_locations_x); i += 1) {
if (ds_list_find_value(tile_locations_x, i) == testpoint_x && ds_list_find_value(tile_locations_y, i) == testpoint_y) {
valid = false;
break;
}
}
if (valid) {
precise_collision_check = true;
if ( collision_rectangle(testpoint_x, testpoint_y, testpoint_x + 16, testpoint_y + 16, obj_horizontal, precise_collision_check, true) == noone &&
collision_rectangle(testpoint_x, testpoint_y, testpoint_x + 16, testpoint_y + 16, obj_vertical, precise_collision_check, true) == noone &&
collision_rectangle(testpoint_x, testpoint_y, testpoint_x + 16, testpoint_y + 16, obj_diagonal1, precise_collision_check, true) == noone &&
collision_rectangle(testpoint_x, testpoint_y, testpoint_x + 16, testpoint_y + 16, obj_diagonal2, precise_collision_check, true) == noone &&
collision_rectangle(testpoint_x, testpoint_y, testpoint_x + 16, testpoint_y + 16, obj_diagonal3, precise_collision_check, true) == noone &&
collision_rectangle(testpoint_x, testpoint_y, testpoint_x + 16, testpoint_y + 16, obj_diagonal4, precise_collision_check, true) == noone &&
collision_rectangle(testpoint_x, testpoint_y, testpoint_x + 16, testpoint_y + 16, obj_diagonalconnector1, precise_collision_check, true) == noone &&
collision_rectangle(testpoint_x, testpoint_y, testpoint_x + 16, testpoint_y + 16, obj_diagonalconnector2, precise_collision_check, true) == noone &&
collision_rectangle(testpoint_x, testpoint_y, testpoint_x + 16, testpoint_y + 16, obj_diagonalconnector3, precise_collision_check, true) == noone &&
collision_rectangle(testpoint_x, testpoint_y, testpoint_x + 16, testpoint_y + 16, obj_diagonalconnector4, precise_collision_check, true) == noone) {
ds_stack_push(tile_stack_x, testpoint_x);
ds_stack_push(tile_stack_y, testpoint_y);
}
}
// above point
testpoint_x = test_location_x;
testpoint_y = test_location_y - 16;
// Check point testpoint_x, testpoint_y is valid
valid = true;
// Check added location list first, then check stack because the same location stackd multiple times doesn't work
for (i = 0; i < ds_list_size(tile_locations_x); i += 1) {
if (ds_list_find_value(tile_locations_x, i) == testpoint_x && ds_list_find_value(tile_locations_y, i) == testpoint_y) {
valid = false;
break;
}
}
if (valid) {
precise_collision_check = true;
if ( collision_rectangle(testpoint_x, testpoint_y, testpoint_x + 16, testpoint_y + 16, obj_horizontal, precise_collision_check, true) == noone &&
collision_rectangle(testpoint_x, testpoint_y, testpoint_x + 16, testpoint_y + 16, obj_vertical, precise_collision_check, true) == noone &&
collision_rectangle(testpoint_x, testpoint_y, testpoint_x + 16, testpoint_y + 16, obj_diagonal1, precise_collision_check, true) == noone &&
collision_rectangle(testpoint_x, testpoint_y, testpoint_x + 16, testpoint_y + 16, obj_diagonal2, precise_collision_check, true) == noone &&
collision_rectangle(testpoint_x, testpoint_y, testpoint_x + 16, testpoint_y + 16, obj_diagonal3, precise_collision_check, true) == noone &&
collision_rectangle(testpoint_x, testpoint_y, testpoint_x + 16, testpoint_y + 16, obj_diagonal4, precise_collision_check, true) == noone &&
collision_rectangle(testpoint_x, testpoint_y, testpoint_x + 16, testpoint_y + 16, obj_diagonalconnector1, precise_collision_check, true) == noone &&
collision_rectangle(testpoint_x, testpoint_y, testpoint_x + 16, testpoint_y + 16, obj_diagonalconnector2, precise_collision_check, true) == noone &&
collision_rectangle(testpoint_x, testpoint_y, testpoint_x + 16, testpoint_y + 16, obj_diagonalconnector3, precise_collision_check, true) == noone &&
collision_rectangle(testpoint_x, testpoint_y, testpoint_x + 16, testpoint_y + 16, obj_diagonalconnector4, precise_collision_check, true) == noone) {
ds_stack_push(tile_stack_x, testpoint_x);
ds_stack_push(tile_stack_y, testpoint_y);
}
}
// below point
testpoint_x = test_location_x;
testpoint_y = test_location_y + 16;
// Check point testpoint_x, testpoint_y is valid
valid = true;
// Check added location list first, then check stack because the same location stackd multiple times doesn't work
for (i = 0; i < ds_list_size(tile_locations_x); i += 1) {
if (ds_list_find_value(tile_locations_x, i) == testpoint_x && ds_list_find_value(tile_locations_y, i) == testpoint_y) {
valid = false;
break;
}
}
if (valid) {
precise_collision_check = true;
if ( collision_rectangle(testpoint_x, testpoint_y, testpoint_x + 16, testpoint_y + 16, obj_horizontal, precise_collision_check, true) == noone &&
collision_rectangle(testpoint_x, testpoint_y, testpoint_x + 16, testpoint_y + 16, obj_vertical, precise_collision_check, true) == noone &&
collision_rectangle(testpoint_x, testpoint_y, testpoint_x + 16, testpoint_y + 16, obj_diagonal1, precise_collision_check, true) == noone &&
collision_rectangle(testpoint_x, testpoint_y, testpoint_x + 16, testpoint_y + 16, obj_diagonal2, precise_collision_check, true) == noone &&
collision_rectangle(testpoint_x, testpoint_y, testpoint_x + 16, testpoint_y + 16, obj_diagonal3, precise_collision_check, true) == noone &&
collision_rectangle(testpoint_x, testpoint_y, testpoint_x + 16, testpoint_y + 16, obj_diagonal4, precise_collision_check, true) == noone &&
collision_rectangle(testpoint_x, testpoint_y, testpoint_x + 16, testpoint_y + 16, obj_diagonalconnector1, precise_collision_check, true) == noone &&
collision_rectangle(testpoint_x, testpoint_y, testpoint_x + 16, testpoint_y + 16, obj_diagonalconnector2, precise_collision_check, true) == noone &&
collision_rectangle(testpoint_x, testpoint_y, testpoint_x + 16, testpoint_y + 16, obj_diagonalconnector3, precise_collision_check, true) == noone &&
collision_rectangle(testpoint_x, testpoint_y, testpoint_x + 16, testpoint_y + 16, obj_diagonalconnector4, precise_collision_check, true) == noone) {
ds_stack_push(tile_stack_x, testpoint_x);
ds_stack_push(tile_stack_y, testpoint_y);
}
}
}
ds_list_destroy(tile_locations_x);
ds_list_destroy(tile_locations_y);
ds_stack_destroy(tile_stack_x);
ds_stack_destroy(tile_stack_y);
It's probably something minor I'm overlooking; I just want a second opinion. Thank you in advance for you help!
-Neil

Related

how do i use getter and setter in flutter?

Im trying to create tic tac toe game, but im getting some logical error when Im trying to use
provider method.
in the home page i have column containing three widget. first for player scores , second for gird layout, third for winner result.
i did some research and i found out using provider calss is the best option to send data from a widget to another.
import 'package:flutter/cupertino.dart';
import 'package:tictactoe/widgets/scores.dart';
import '../widgets/layout.dart';
// import '../widgets/scores.dart';
class GameProvider with ChangeNotifier {
bool oTrue = true;
List displayXO = ['', '', '', '', '', '', '', '', ''];
String resultDetection = 'hey';
int oScores = 0;
int xScores = 0;
int failedScores = 0;
checkWinner() {
// checking 1 row
if ((displayXO[0] == displayXO[1]) &&
(displayXO[0] == displayXO[2]) &&
displayXO[0] != '') {
resultDetection = 'player ' + displayXO[0] + 'wins';
print('$resultDetection');
}
// checking 2 row
if ((displayXO[3] == displayXO[4]) &&
(displayXO[3] == displayXO[5]) &&
(displayXO[3] != '')) {
resultDetection = 'player ' + displayXO[3] + 'wins';
}
// checking 3 row
if ((displayXO[6] == displayXO[7]) &&
(displayXO[6] == displayXO[8]) &&
(displayXO[6] != '')) {
resultDetection = 'player ' + displayXO[6] + 'wins';
}
// checking 1 column
if ((displayXO[0] == displayXO[3]) &&
(displayXO[0] == displayXO[6]) &&
(displayXO[0] != '')) {
resultDetection = 'player ' + displayXO[0] + 'wins';
}
// checking 2 column
if ((displayXO[1] == displayXO[4]) &&
(displayXO[1] == displayXO[7]) &&
(displayXO[1] != '')) {
resultDetection = 'player ' + displayXO[1] + 'wins';
}
// checking 3 column
if ((displayXO[2] == displayXO[5]) &&
(displayXO[2] == displayXO[8]) &&
(displayXO[2] != '')) {
resultDetection = 'player ' + displayXO[2] + 'wins';
}
// checking 1 axis
if ((displayXO[6] == displayXO[4]) &&
(displayXO[6] == displayXO[2]) &&
(displayXO[6] != '')) {
resultDetection = 'player ' + displayXO[6] + 'wins';
}
// checking 2 axis
if ((displayXO[0] == displayXO[4]) &&
(displayXO[0] == displayXO[8]) &&
(displayXO[0] != '')) {
resultDetection = 'player ' + displayXO[0] + 'wins';
}
notifyListeners();
}
set upgrateScores(String winner) {
if (winner == 'O') {
oScores++;
} else if (winner == 'X') {
xScores++;
}
print('$oScores, $xScores');
notifyListeners();
}
get winnerfunction => checkWinner();
// get upgrarate => upgrateScores();
get result => resultDetection;
}
and this is my great layout
Widget build(BuildContext context) {
void _tap() {}
return GridView.builder(
itemCount: 9,
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
itemBuilder: ((context, index) {
return GestureDetector(
onTap: () {
tapped(index);
},
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
border: Border.all(width: 5, color: Colors.red),
color: Colors.yellow,
),
child: Center(
child: Text(
displayXO[index],
style: GoogleFonts.coiny(fontSize: 64, color: Colors.red),
),
),
),
);
}));
}
void tapped(int tap) {
setState(() {
if (oTrue && displayXO[tap] == '') {
displayXO[tap] = 'O';
oTrue = false;
} else if (!oTrue && displayXO[tap] == '') {
displayXO[tap] = 'X';
oTrue = true;
}
print('object');
GameProvider provider = Provider.of<GameProvider>(context, listen: false);
provider.checkWinner();
});
}

How to format user's input text to MM/DD/YYYY using Input Formatter in Flutter

Need to know how to format the user's input text to "MM/DD/YYYY" format. As the user enters the value in input field, the text should be taken in this format, plus let's say if the users type the month as 14, the "4" should not be added. This month, date, year validation needs to be happening as the users types.
This is what I have so far, The month formatting works fine, but needs improvements in date formatting.
class _DateFormatter extends TextInputFormatter {
#override
TextEditingValue formatEditUpdate(
TextEditingValue prevText, TextEditingValue currText) {
int selectionIndex;
// Get the previous and current input strings
String pText = prevText.text;
String cText = currText.text;
// Abbreviate lengths
int cLen = cText.length;
int pLen = pText.length;
//Date
if (cLen == 1) {
// Can only be 0, 1, 2 or 3
if (int.parse(cText) > 1) {
// Remove char
cText = '';
}
} else if (cLen == 2 && pLen == 1) {
// Can only be 0, 1, 2 or 3
if (int.parse(cText) == 0 || int.parse(cText) > 12) {
// Remove char
cText = cText.substring(0, 1);
} else {
cText += '/';
}
} else if (cLen == 2 && pLen == 3) {
cText = cText.substring(0, 1);
} else if (cLen == 2 && pLen == 4) {
cText += '/';
} else if (cLen == 3 && pLen == 2) {
cText = cText.substring(0, 2) + '/' + cText.substring(2, 3);
}
// Month
else if (cLen == 4 && pLen == 2) {
print('s');
if (int.parse(cText.substring(3, 4)) > 3) {
cText = cText;
}
} else if (cLen == 4 && pLen == 3) {
if (int.parse(cText.substring(3, 4)) > 3) {
cText = cText.substring(0, 3);
}
// // Can only be 0, 1, 2 or 3
// if (int.parse(cText) > 3) {
// // Remove char
// cText = '
// }
// // Can only be 0 or 1
// if (int.parse(cText.substring(3, 4)) > 1) {
// // Remove char
// cText = cText.substring(0, 3);
// }
} else if (cLen == 5 && pLen == 4) {
// Month cannot be greater than 12
int mm = int.parse(cText.substring(3, 5));
if (mm == 0 || mm > 12) {
// Remove char
cText = cText.substring(0, 4);
} else {
// Add a / char
cText += '/';
}
} else if ((cLen == 3 && pLen == 4) || (cLen == 6 && pLen == 7)) {
// Remove / char
cText = cText.substring(0, cText.length - 1);
} else if (cLen == 3 && pLen == 2) {
if (int.parse(cText.substring(2, 3)) > 1) {
// Replace char
cText = cText.substring(0, 2) + '/';
} else {
// Insert / char
cText =
cText.substring(0, pLen) + '/' + cText.substring(pLen, pLen + 1);
}
}
//Year
else if (cLen == 6 && pLen == 5) {
// Can only be 1 or 2 - if so insert a / char
int y1 = int.parse(cText.substring(5, 6));
if (y1 < 1 || y1 > 2) {
// Replace char
cText = cText.substring(0, 5) + '/';
} else {
// Insert / char
cText = cText.substring(0, 5) + '/' + cText.substring(5, 6);
}
} else if (cLen == 7) {
// Can only be 1 or 2
int y1 = int.parse(cText.substring(6, 7));
if (y1 < 1 || y1 > 2) {
// Remove char
cText = cText.substring(0, 6);
}
} else if (cLen == 8) {
// Can only be 19 or 20
int y2 = int.parse(cText.substring(6, 8));
if (y2 < 19 || y2 > 20) {
// Remove char
cText = cText.substring(0, 7);
}
}
selectionIndex = cText.length;
print(selectionIndex);
return TextEditingValue(
text: cText,
selection: TextSelection.collapsed(offset: selectionIndex),
);
}
}
Can you try to follow this package, I hope it would be helpful
https://pub.dev/packages/mask_text_input_formatter
var textEditingController = TextEditingController(text: "12345678");
var maskFormatter = new MaskTextInputFormatter(mask: '####-####', filter: { "#": RegExp(r'[0-9]') });
TextField(controller: textEditingController, inputFormatters: [maskFormatter]) // -> "1234-5678"
textEditingController.value = maskFormatter.updateMask(mask: "##-##-##-##");

Flutter TextField input validation for a date

I am trying to write a date input control which accepts a date like 23/12/1997. What I would like it to do is automatically insert the / characters for the user. So as they type in 23 the listener returns 23/, so that they can then type in 12. At this point the listener again adds a / leaving the user to complete the date by typing 1997.
My TextEditingController code half works and looks like this:
final _controller = TextEditingController();
_controller.addListener(() {
String text = _controller.text;
if (text.length == 2) {
text += '/';
}
if (text.length == 5) {
text += '/';
}
_controller.value = _controller.value.copyWith(
text: text,
selection:
TextSelection(baseOffset: text.length, extentOffset: text.length),
composing: TextRange.empty,
);
print(_controller.text);
}
So it works fine until the user makes a mistake and needs to backtrack. As soon as a / is deleted it is immediately replaced stopping any further editing of the date.
In order to get it to work I need to access is the previously entered text to determine if the user is backspacing. So if text == 23/ && previous_text == 23/1 then I can remove the / from text.
I found this question textfield must only accept numbers and I think it may help me, but I am not sure how to implement an existing widget and override its methods. Of course there may be a simpler way to do this within the TextEditingController?
I have found what I needed to solve my date validation input. It is not perfect, but it is good enough for what I am trying to do. All I needed was to look at the inputFormatters method of a TextField(). This allow manipulation of the input text to put it into any number of user-defined formats. I am including a segment of my code for anyone who would like to try it out:
class _DateFormatter extends TextInputFormatter {
#override
TextEditingValue formatEditUpdate(
TextEditingValue prevText, TextEditingValue currText) {
int selectionIndex;
// Get the previous and current input strings
String pText = prevText.text;
String cText = currText.text;
// Abbreviate lengths
int cLen = cText.length;
int pLen = pText.length;
if (cLen == 1) {
// Can only be 0, 1, 2 or 3
if (int.parse(cText) > 3) {
// Remove char
cText = '';
}
} else if (cLen == 2 && pLen == 1) {
// Days cannot be greater than 31
int dd = int.parse(cText.substring(0, 2));
if (dd == 0 || dd > 31) {
// Remove char
cText = cText.substring(0, 1);
} else {
// Add a / char
cText += '/';
}
} else if (cLen == 4) {
// Can only be 0 or 1
if (int.parse(cText.substring(3, 4)) > 1) {
// Remove char
cText = cText.substring(0, 3);
}
} else if (cLen == 5 && pLen == 4) {
// Month cannot be greater than 12
int mm = int.parse(cText.substring(3, 5));
if (mm == 0 || mm > 12) {
// Remove char
cText = cText.substring(0, 4);
} else {
// Add a / char
cText += '/';
}
} else if ((cLen == 3 && pLen == 4) || (cLen == 6 && pLen == 7)) {
// Remove / char
cText = cText.substring(0, cText.length - 1);
} else if (cLen == 3 && pLen == 2) {
if (int.parse(cText.substring(2, 3)) > 1) {
// Replace char
cText = cText.substring(0, 2) + '/';
} else {
// Insert / char
cText =
cText.substring(0, pLen) + '/' + cText.substring(pLen, pLen + 1);
}
} else if (cLen == 6 && pLen == 5) {
// Can only be 1 or 2 - if so insert a / char
int y1 = int.parse(cText.substring(5, 6));
if (y1 < 1 || y1 > 2) {
// Replace char
cText = cText.substring(0, 5) + '/';
} else {
// Insert / char
cText = cText.substring(0, 5) + '/' + cText.substring(5, 6);
}
} else if (cLen == 7) {
// Can only be 1 or 2
int y1 = int.parse(cText.substring(6, 7));
if (y1 < 1 || y1 > 2) {
// Remove char
cText = cText.substring(0, 6);
}
} else if (cLen == 8) {
// Can only be 19 or 20
int y2 = int.parse(cText.substring(6, 8));
if (y2 < 19 || y2 > 20) {
// Remove char
cText = cText.substring(0, 7);
}
}
selectionIndex = cText.length;
return TextEditingValue(
text: cText,
selection: TextSelection.collapsed(offset: selectionIndex),
);
}
}
To use it simply call it from the Textfield() as shown below. I've also incorporated two built in methods as well. WhitelistingTextInputFormatter() to only allow digits and a slash(/) character to be entered and LengthLimitingTextInputFormatter() to restrict the number of characters allowed. The latter could be achieved using the maxLength parameter of TextField() but it is here by way of example. Note that there is also a BlacklistingTextInputFormatter() which does as you would expect.
WhitelistingTextInputFormatter was removed use FilteringTextInputFormatter.allow(RegExp("[0-9-]")), to replace, and If you want to change split symbol (current is "/"), Pls add it to RegExp(....).
TextField(
// maxLength: 10,
keyboardType: TextInputType.datetime,
controller: _controllerDOB,
focusNode: _focusNodeDOB,
decoration: InputDecoration(
hintText: 'DD/MM/YYYY',
counterText: '',
),
inputFormatters: [
WhitelistingTextInputFormatter(RegExp("[0-9/]")),
LengthLimitingTextInputFormatter(10),
_DateFormatter(),
],
),
You can use datepicker dialog made by flutter.
DateTime _date = DateTime.now()
onPressed: () {
showDatePicker(
context: context,
initialDate: _date,
firstDate: DateTime(2020),
lastDate: DateTime(2021),
).then((date) {
setState(() {
_date = date;
});
});
},
I find your code to be a great utility, without any dependencies. I took the liberty to do a few mods and thought of posting it back here, as I find your concept very neat and lightweight on the UI. The requirements were;
Validating the date for non-31-day months and leap years. The mods were quite straightforward.
Preventing the user entering "/" at undesirable places which will throw the algorithm off-track. The simplest solution is to make the
keyboardType: TextInputType.number, in the TextField
This works perfectly for mobile devices.
But Flutter being cross-platform, this solution may not be foolproof when it comes to a device with physical keyboard. I tried various checks and blocks but only partially succeeded; i.e, user can still input "/" in between the digits of the Day and Month. I think there is an internal delay between KB input and programmatic formatter.
Following is the modified code for _DateFormatter. I have used the /// notion to distinguish my comments. They should be read along with the original // comments.
class _DateFormatter extends TextInputFormatter {
#override
TextEditingValue formatEditUpdate(
TextEditingValue prevText, TextEditingValue currText) {
int selectionIndex;
String date;
String month;
int year;
// Get the previous and current input strings
String pText = prevText.text;
String cText = currText.text;
cText = cText.replaceAll("//", "/");
// Abbreviate lengths
int cLen = cText.length;
int pLen = pText.length;
/// ENTERING THE DATE
if (cLen == 1) {
/// User enters the first digit of the date. The first digit
// Can only be 0, 1, 2 or 3
if (int.parse(cText) > 3) {
// Remove char
cText = '';
}
} else if (cLen == 2 && pLen == 1) {
/// User has already entered a valid first digit of the date, now he
/// enters the second digit of the date; but
// Days cannot be greater than 31
int dd = int.parse(cText.substring(0, 2));
if (dd == 0 || dd > 31) {
// Remove char
cText = cText.substring(0, 1);
} else {
/// User has entered a valid date (between 1 and 31). So now,
// Add a / char
cText += '/';
}
/// ENTERING THE MONTH
} else if (cLen == 4) {
/// after entering a valid date and programmatic insertion of '/', now User has entered
/// the first digit of the Month. But, it
// Can only be 0 or 1
/// (and, not '/' either)
if (int.parse(cText.substring(3, 4)) > 1 || cText.substring(3, 4) == "/") {
// Remove char
cText = cText.substring(0, 3);
}
} else if (cLen == 5 && pLen == 4) {
int mm = int.parse(cText.substring(3, 5));
int dd = int.parse(cText.substring(0, 2));
/// User has entered the second digit of the Month, but the
// Month cannot be greater than 12
/// Also, that entry cannot be '/'
if ((mm == 0 || mm > 12|| cText.substring(3, 5) == "/") ||
/// If the date is 31, the month cannot be Apr, Jun, Sept or Nov
(dd == 31 && (mm == 02 || mm == 04 || mm == 06 || mm == 09 || mm == 11)) ||
/// If the date is greater than 29, the month cannot be Feb
/// (Leap years will be dealt with, when user enters the Year)
(dd > 29 && (mm == 02))) {
// Remove char
cText = cText.substring(0, 4);
}
else if (cText.length == 5) {
/// the Month entered is valid; so,
// Add a / char
cText += '/';
}
} else if ((cLen == 3 && pLen == 4) || (cLen == 6 && pLen == 7)) {
// Remove / char
cText = cText.substring(0, cText.length - 1);
} else if (cLen == 3 && pLen == 2) {
if (int.parse(cText.substring(2, 3)) > 1) {
// Replace char
cText = cText.substring(0, 2) + '/';
} else {
// Insert / char
cText =
cText.substring(0, pLen) + '/' + cText.substring(pLen, pLen + 1);
}
/// ENTERING THE YEAR
} else if (cLen == 6 && pLen == 5) {
// Can only be 1 or 2 - if so insert a / char
int y1 = int.parse(cText.substring(5, 6));
if (y1 < 1 || y1 > 2) {
// Replace char
/// i.e, add '/' after the 5th position
cText = cText.substring(0, 5) + '/';
} else {
// Insert / char
cText = cText.substring(0, 5) + '/' + cText.substring(5, 6);
}
} else if (cLen == 7) {
/// the first digit of year
// Can only be 1 or 2
int y1 = int.parse(cText.substring(6, 7));
if (y1 < 1 || y1 > 2) {
// Remove char
cText = cText.substring(0, 6);
}
} else if (cLen == 8) {
// Can only be 19 or 20
/// Also, there cannot be / typed by the user
String y2 = cText.substring(6, 8);
if (y2 != "19" && y2 != "20") {
// Remove char
cText = cText.substring(0, 7);
}
} else if (cLen == 9) {
/// There cannot be / typed by the user
if (cText.substring(8, 9) == "/") {
// Remove char
cText = cText.substring(0, 8);
}
} else if (cLen == 10) {
/// There cannot be / typed by the user
if (cText.substring(9, 10) == "/") {
// Remove char
cText = cText.substring(0, 9);
}
/// If the year entered is not a leap year but the date entered is February 29,
/// it will be advanced to the next valid date
date = cText.substring(0, 2);
month = cText.substring(3, 5);
year = int.parse(cText.substring(6, 10));
bool isNotLeapYear = !((year % 4 == 0) && (year % 100 != 0) ||
(year % 400 == 0));
if (isNotLeapYear && month == "02" && date == "29") {
cText = "01/03/$year";
}
}
selectionIndex = cText.length;
return TextEditingValue(
text: cText,
selection: TextSelection.collapsed(offset: selectionIndex),
);
}
} // END OF class _DateFormatter
I found one solution for this, But not an optimized solution, but it is covering almost all the scenarios,
forward slash during adding fields
remove the forward slash on clearing fields
in between editing handling... etc
class CustomDateTextFormatter extends TextInputFormatter {
#override
TextEditingValue formatEditUpdate(
TextEditingValue oldValue, TextEditingValue newValue) {
var text = _format(newValue.text, '/', oldValue);
return newValue.copyWith(
text: text, selection: _updateCursorPosition(text, oldValue));
}
}
String _format(String value, String seperator, TextEditingValue old) {
var finalString = '';
var dd = '';
var mm = '';
var yyy = '';
var oldVal = old.text;
print('<------------------------- start---------------------------->');
print('oldVal -> $oldVal');
print('value -> $value');
var temp_oldVal = oldVal;
var temp_value = value;
if (!oldVal.contains(seperator) ||
oldVal.isEmpty ||
seperator.allMatches(oldVal).length < 2) {
oldVal += '///';
}
if (!value.contains(seperator) || _backSlashCount(value) < 2) {
value += '///';
}
var splitArrOLD = oldVal.split(seperator);
var splitArrNEW = value.split(seperator);
print('----> splitArrOLD: $splitArrOLD');
print('----> splitArrNEW: $splitArrNEW');
for (var i = 0; i < 3; i++) {
splitArrOLD[i] = splitArrOLD[i].toString().trim();
splitArrNEW[i] = splitArrNEW[i].toString().trim();
}
// block erasing
if ((splitArrOLD[0].isNotEmpty &&
splitArrOLD[2].isNotEmpty &&
splitArrOLD[1].isEmpty &&
temp_value.length < temp_oldVal.length &&
splitArrOLD[0] == splitArrNEW[0] &&
splitArrOLD[2].toString().trim() ==
splitArrNEW[1].toString().trim()) ||
(_backSlashCount(temp_oldVal) > _backSlashCount(temp_value) &&
splitArrNEW[1].length > 2) ||
(splitArrNEW[0].length > 2 && _backSlashCount(temp_oldVal) == 1) ||
(_backSlashCount(temp_oldVal) == 2 &&
_backSlashCount(temp_value) == 1 &&
splitArrNEW[0].length > splitArrOLD[0].length)) {
finalString = temp_oldVal; // making the old date as it is
print('blocked finalString : $finalString ');
} else {
if (splitArrNEW[0].length > splitArrOLD[0].length) {
if (splitArrNEW[0].length < 3) {
dd = splitArrNEW[0];
} else {
for (var i = 0; i < 2; i++) {
dd += splitArrNEW[0][i];
}
}
if (dd.length == 2 && !dd.contains(seperator)) {
dd += seperator;
}
} else if (splitArrNEW[0].length == splitArrOLD[0].length) {
print('splitArrNEW[0].length == 2');
if (oldVal.length > value.length && splitArrNEW[1].isEmpty) {
dd = splitArrNEW[0];
} else {
dd = splitArrNEW[0] + seperator;
}
} else if (splitArrNEW[0].length < splitArrOLD[0].length) {
print('splitArrNEW[0].length < splitArrOLD[0].length');
if (oldVal.length > value.length &&
splitArrNEW[1].isEmpty &&
splitArrNEW[0].isNotEmpty) {
dd = splitArrNEW[0];
} else if (temp_oldVal.length > temp_value.length &&
splitArrNEW[0].isEmpty &&
_backSlashCount(temp_value) == 2) {
dd += seperator;
} else {
if (splitArrNEW[0].isNotEmpty) {
dd = splitArrNEW[0] + seperator;
}
}
}
print('dd value --> $dd');
if (dd.isNotEmpty) {
finalString = dd;
if (dd.length == 2 &&
!dd.contains(seperator) &&
oldVal.length < value.length &&
splitArrNEW[1].isNotEmpty) {
if (seperator.allMatches(dd).isEmpty) {
finalString += seperator;
}
} else if (splitArrNEW[2].isNotEmpty &&
splitArrNEW[1].isEmpty &&
temp_oldVal.length > temp_value.length) {
if (seperator.allMatches(dd).isEmpty) {
finalString += seperator;
}
} else if (oldVal.length < value.length &&
(splitArrNEW[1].isNotEmpty || splitArrNEW[2].isNotEmpty)) {
if (seperator.allMatches(dd).isEmpty) {
finalString += seperator;
}
}
} else if (_backSlashCount(temp_oldVal) == 2 && splitArrNEW[1].isNotEmpty) {
dd += seperator;
}
print('finalString after dd=> $finalString');
if (splitArrNEW[0].length == 3 && splitArrOLD[1].isEmpty) {
mm = splitArrNEW[0][2];
}
if (splitArrNEW[1].length > splitArrOLD[1].length) {
print('splitArrNEW[1].length > splitArrOLD[1].length');
if (splitArrNEW[1].length < 3) {
mm = splitArrNEW[1];
} else {
for (var i = 0; i < 2; i++) {
mm += splitArrNEW[1][i];
}
}
if (mm.length == 2 && !mm.contains(seperator)) {
mm += seperator;
}
} else if (splitArrNEW[1].length == splitArrOLD[1].length) {
print('splitArrNEW[1].length = splitArrOLD[1].length');
if (splitArrNEW[1].isNotEmpty) {
mm = splitArrNEW[1];
}
} else if (splitArrNEW[1].length < splitArrOLD[1].length) {
print('splitArrNEW[1].length < splitArrOLD[1].length');
if (splitArrNEW[1].isNotEmpty) {
mm = splitArrNEW[1] + seperator;
}
}
print('mm value --> $mm');
if (mm.isNotEmpty) {
finalString += mm;
if (mm.length == 2 && !mm.contains(seperator)) {
if (temp_oldVal.length < temp_value.length) {
finalString += seperator;
}
}
}
print('finalString after mm=> $finalString');
if (splitArrNEW[1].length == 3 && splitArrOLD[2].isEmpty) {
yyy = splitArrNEW[1][2];
}
if (splitArrNEW[2].length > splitArrOLD[2].length) {
print('splitArrNEW[2].length > splitArrOLD[2].length');
if (splitArrNEW[2].length < 5) {
yyy = splitArrNEW[2];
} else {
for (var i = 0; i < 4; i++) {
yyy += splitArrNEW[2][i];
}
}
} else if (splitArrNEW[2].length == splitArrOLD[2].length) {
print('splitArrNEW[2].length == splitArrOLD[2].length');
if (splitArrNEW[2].isNotEmpty) {
yyy = splitArrNEW[2];
}
} else if (splitArrNEW[2].length < splitArrOLD[2].length) {
print('splitArrNEW[2].length < splitArrOLD[2].length');
yyy = splitArrNEW[2];
}
print('yyy value --> $yyy');
if (yyy.isNotEmpty) {
if (_backSlashCount(finalString) < 2) {
if (splitArrNEW[0].isEmpty && splitArrNEW[1].isEmpty) {
finalString = seperator + seperator + yyy;
} else {
finalString = finalString + seperator + yyy;
}
} else {
finalString += yyy;
}
} else {
if (_backSlashCount(finalString) > 1 && oldVal.length > value.length) {
var valueUpdate = finalString.split(seperator);
finalString = valueUpdate[0] + seperator + valueUpdate[1];
}
}
print('finalString after yyyy=> $finalString');
}
print('<------------------------- finish---------------------------->');
return finalString;
}
TextSelection _updateCursorPosition(String text, TextEditingValue oldValue) {
var endOffset = max(
oldValue.text.length - oldValue.selection.end,
0,
);
var selectionEnd = text.length - endOffset;
print('My log ---> $selectionEnd');
return TextSelection.fromPosition(TextPosition(offset: selectionEnd));
}
int _backSlashCount(String value) {
return '/'.allMatches(value).length;
}
We can Use our custom formator as in inputFormatters like below
TextField(
// maxLength: 10,
keyboardType: TextInputType.datetime,
controller: _controllerDOB,
focusNode: _focusNodeDOB,
decoration: InputDecoration(
hintText: 'DD/MM/YYYY',
counterText: '',
),
inputFormatters: [
WhitelistingTextInputFormatter(RegExp("[0-9/]")),
LengthLimitingTextInputFormatter(10),
CustomDateTextFormatter(),
],
),
try out this solution.

Sending Email on Form Submission After Gathering Data from A Different Spreadsheet

I need some help fixing my current code. Here is what I am trying to do:
1. Have user submit a Google form (name, class, etc.)
2. Take those answers and search a separate spreadsheet for matching information (same name, class, etc.)
3. Return data from the same row the matching data is found in (parents name, email, etc.)
4. Send an email to the parents containing information from the original Google form
I think where I have mistakes is the part of the ode that attempts to open the separate sheet by ID and search and return values. I've included this section of the code below.
Does anyone have any suggestions? All are welcome! And thank you for your time :)
var sh = SpreadsheetApp.openById("17aA0SMZe1tarCUWM0ROdFmGoE3xsqrFRtYviHb85cqM");
Logger.log(sh.getName());
var sheet = sh.getActiveSheet();
var dataRange = sheet.getDataRange();
var values = dataRange.getValues();
for(var i = 0; i < values.length; i++) {
if(values[i][0] == dancersFirstName && values[i][1] == dancersLastName && values[i][2] == class)
{return values[i][7];}
var primaryContactFirstName = values[i][7];
if(values[i][0] == dancersFirstName && values[i][1] == dancersLastName && values[i][2] == class)
{return values[i][8];}
var primaryContactLastName = values[i][8];
if(values[i][0] == dancersFirstName && values[i][1] == dancersLastName && values[i][2] == class)
{return values[i][9];}
var primaryContactEmail = values[i][9];}
if(dancersFirstName + dancersLastName + class == values[i][0] + values[i][1] + values[i][2], toEmailAddress = primaryContactEmail);
The entire code is as follows:
function onFormSubmit(e) {
var timestamp = e.values[0];
var dancersFirstName = e.values[1];
var dancersLastName = e.values[2];
var class = e.values[3];
var teacher = e.values[4]
var attentiveness = e.values[5]
var bodyPlacement = e.values[6];
var memory = e.values[7];
var corrections = e.values[8];
var performance = e.values[9];
var attendance = e.values[10];
var comments = e.values[11];
var placement = e.values[12];
var teachersEmailAddress = e.values[13];
var toEmailAddress = teachersEmailAddress;
var sh = SpreadsheetApp.getActiveSpreadsheet().openbyID("17aA0SMZe1tarCUWM0ROdFmGoE3xsqrFRtYviHb85cqM");
var values = sh.getDataRange().getValues();
for(var i=0, iLen=values.length; i<iLen; i++) {
if(values[i][0] == dancersFirstName && values[i][1] == dancersLastName && values[i][2] == class)
{return values[i][7];}
var primaryContactFirstName = values[i][7];
if(values[i][0] == dancersFirstName && values[i][1] == dancersLastName && values[i][2] == class)
{return values[i][8];}
var primaryContactLastName = values[i][8];
if(values[i][0] == dancersFirstName && values[i][1] == dancersLastName && values[i][2] == class)
{return values[i][9];}
var primaryContactEmail = values[i][9];
if(dancersFirstName + dancersLastName + class == values[i][0] + values[i][1] + values[i][2], toEmailAddress = primaryContactEmail);}
var subject = "2015-2016 Evaluation & Placement";
var emailBody = "Dear" + dancersFirstName + " " + dancersLastName + " "+ "and" + primaryContactFirstName +" " + primaryContactLastName + " " +
"Below is your dancer's evaluation and pleacement for" + class +
"\n\nIs this dancer focused and attentive in class?" + attentiveness +
"\n\nDoes this dancer understand and work towards correct body placement and alignment?" + bodyPlacement +
"\n\nHas this dancer mastered the steps taught this year? Is the dancer able to remember combinations?" + memory +
"\n\nIs this dancer able to apply corrections?" + corrections +
"\n\nIs this dancer able to dance with the music, perform with confidence, and display a commitment to performance quality?" + performance +
"\n\nHas this dancer attended class regularly?" + attendance +
"\n\nAdditonal Comments:" + comments +
"\n\nPlacement for next year:" + placement +
"\n\nThank you for dancing with us and enjoy your summer!" + teacher + "and The Moving Company Dance Center";
MailApp.sendEmail(toEmailAddress,subject,emailBody);}

Inconsistent indent QGIS

I try to set some qwidgets on fields in a Qgis Project, but i have an error of inconsistent dedent at elif field.name() == 'syst':. Made so far:
def load(self):
map_layers = iface.legendInterface().layers()
for layer in map_layers:
if layer.name() == 'MAT_CANA':
mat_canaId = layer.id()
elif layer.name() == 'Collecteur':
CollecteurId = layer.id()
elif layer.name() == 'FORME_CHAMBRE':
FormeChambreId = layer.id()
elif layer.name() == 'Genre_Chambre':
GenreChambreId = layer.id()
elif layer.name() == 'MAT_CHMB_COU':
MaterialChambreCouId = layer.id()
elif layer.name() == 'PRECISION':
PrecisionId = layer.id()
elif layer.name() == 'SYSTEM_ECOULE':
SystemEcouleId = layer.id()
for layer in map_layers:
if '"public"."chambre_eau_provisoire"' in layer.source():
layer.setEditForm('C:/Users/george.vina/Desktop/BOUDRY/Forme/formeUi/chambrePr.ui')
fields = layer.pendingFields()
for field in fields:
if field.name() == 'genre_cham':
fieldIndex = layer.fieldNameIndex('genre_cham')
layer.setEditorWidgetV2(fieldIndex,'ValueRelation')
layer.setEditorWidgetV2Config(fieldIndex,{'Layer': GenreChambreId,'Key': 'genre_chambre', 'Value': 'genre_chambre'})
elif field.name() == 'syst':
fieldIndex = layer.fieldNameIndex('syst')
layer.setEditorWidgetV2(fieldIndex,'ValueRelation')
layer.setEditorWidgetV2Config(fieldIndex,{'Layer': SystemEcouleId,'Key': 'system_ecoule', 'Value': 'system_ecoule'})
elif field.name() == 'type_chamb':
fieldIndex = layer.fieldNameIndex('type_chamb')
layer.setEditorWidgetV2(fieldIndex,'ValueRelation')
layer.setEditorWidgetV2Config(fieldIndex,{'Layer': FormeChambreId,'Key': 'forme_chambre', 'Value': 'forme_chambre'})
elif field.name() == 'mat_chamb':
fieldIndex = layer.fieldNameIndex('mat_chamb')
layer.setEditorWidgetV2(fieldIndex,'ValueRelation')
layer.setEditorWidgetV2Config(fieldIndex,{'Layer': MaterialChambreCouId,'Key': 'mat_chmb_cou', 'Value': 'mat_chmb_cou'})
elif field.name() == 'type_couve':
fieldIndex = layer.fieldNameIndex('type_couve')
layer.setEditorWidgetV2(fieldIndex,'ValueRelation')
layer.setEditorWidgetV2Config(fieldIndex,{'Layer': MaterialChambreCouId,'Key': 'mat_chmb_cou', 'Value': 'mat_chmb_cou'})