Allow rich-text in feedback

This commit is contained in:
2025-06-10 15:50:43 +02:00
parent 6dc88285d0
commit f7b4f29e2e
3 changed files with 44 additions and 27 deletions

View File

@ -126,14 +126,15 @@ fun groupTaskWidget(
Row { Row {
DateTimePicker(deadline, onSetDeadline) DateTimePicker(deadline, onSetDeadline)
} }
RichTextStyleRow(state = updTask) RichTextField(updTask, Modifier.fillMaxWidth().weight(1f)) { Text("Task") }
OutlinedRichTextEditor( // RichTextStyleRow(state = updTask)
state = updTask, // OutlinedRichTextEditor(
modifier = Modifier.fillMaxWidth().weight(1f), // state = updTask,
singleLine = false, // modifier = Modifier.fillMaxWidth().weight(1f),
minLines = 5, // singleLine = false,
label = { Text("Task") } // minLines = 5,
) // label = { Text("Task") }
// )
CancelSaveRow( CancelSaveRow(
true, true,
{ updTask.setMarkdown(taskMD) }, { updTask.setMarkdown(taskMD) },
@ -276,16 +277,20 @@ fun groupFeedbackPane(
key: Any? = null key: Any? = null
) { ) {
var grade by remember(globFeedback, key) { mutableStateOf(globFeedback?.grade ?: "") } var grade by remember(globFeedback, key) { mutableStateOf(globFeedback?.grade ?: "") }
var feedback by remember(currentCriterion, criteria, criterionFeedback, key) { mutableStateOf(TextFieldValue(criterionFeedback?.feedback ?: "")) } val feedback = rememberRichTextState()
LaunchedEffect(currentCriterion, criteria, criterionFeedback, key) {
feedback.setMarkdown(criterionFeedback?.feedback ?: "")
}
Column(modifier) { Column(modifier) {
Row { Row {
Text("Overall grade: ", Modifier.align(Alignment.CenterVertically)) Text("Overall grade: ", Modifier.align(Alignment.CenterVertically))
OutlinedTextField(grade, { grade = it }, Modifier.weight(0.2f)) OutlinedTextField(grade, { grade = it }, Modifier.weight(0.2f))
Spacer(Modifier.weight(0.6f)) Spacer(Modifier.weight(0.6f))
Button( Button(
{ onSetGrade(grade); onSetFeedback(feedback.text) }, { onSetGrade(grade); onSetFeedback(feedback.toMarkdown()) },
Modifier.weight(0.2f).align(Alignment.CenterVertically), Modifier.weight(0.2f).align(Alignment.CenterVertically)
enabled = grade.isNotBlank() || feedback.text.isNotBlank()
) { ) {
Text("Save") Text("Save")
} }
@ -297,11 +302,7 @@ fun groupFeedbackPane(
} }
} }
Spacer(Modifier.height(5.dp)) Spacer(Modifier.height(5.dp))
AutocompleteLineField( RichTextField(feedback, Modifier.fillMaxWidth().weight(1f)) { Text("Feedback") }
feedback, { feedback = it }, Modifier.fillMaxWidth().weight(1f), { Text("Feedback") }
) { filter ->
autofill.filter { x -> x.trim().startsWith(filter.trim()) }
}
} }
} }
@ -480,16 +481,19 @@ fun soloFeedbackPane(
key: Any? = null key: Any? = null
) { ) {
var grade by remember(globFeedback, key) { mutableStateOf(globFeedback?.grade ?: "") } var grade by remember(globFeedback, key) { mutableStateOf(globFeedback?.grade ?: "") }
var feedback by remember(currentCriterion, criteria, key) { mutableStateOf(TextFieldValue(criterionFeedback?.feedback ?: "")) } val feedback = rememberRichTextState()
LaunchedEffect(currentCriterion, criteria, criterionFeedback, key) {
feedback.setMarkdown(criterionFeedback?.feedback ?: "")
}
Column(modifier) { Column(modifier) {
Row { Row {
Text("Overall grade: ", Modifier.align(Alignment.CenterVertically)) Text("Overall grade: ", Modifier.align(Alignment.CenterVertically))
OutlinedTextField(grade, { grade = it }, Modifier.weight(0.2f)) OutlinedTextField(grade, { grade = it }, Modifier.weight(0.2f))
Spacer(Modifier.weight(0.6f)) Spacer(Modifier.weight(0.6f))
Button( Button(
{ onSetGrade(grade); onSetFeedback(feedback.text) }, { onSetGrade(grade); onSetFeedback(feedback.toMarkdown()) },
Modifier.weight(0.2f).align(Alignment.CenterVertically), Modifier.weight(0.2f).align(Alignment.CenterVertically)
enabled = grade.isNotBlank() || feedback.text.isNotBlank()
) { ) {
Text("Save") Text("Save")
} }
@ -501,11 +505,7 @@ fun soloFeedbackPane(
} }
} }
Spacer(Modifier.height(5.dp)) Spacer(Modifier.height(5.dp))
AutocompleteLineField( RichTextField(feedback, Modifier.fillMaxWidth().weight(1f)) { Text("Feedback") }
feedback, { feedback = it }, Modifier.fillMaxWidth().weight(1f), { Text("Feedback") }
) { filter ->
autofill.filter { x -> x.trim().startsWith(filter.trim()) }
}
} }
} }

View File

@ -28,6 +28,7 @@ import androidx.compose.ui.unit.sp
import com.jaytux.grader.loadClipboard import com.jaytux.grader.loadClipboard
import com.jaytux.grader.toClipboard import com.jaytux.grader.toClipboard
import com.mohamedrejeb.richeditor.model.RichTextState import com.mohamedrejeb.richeditor.model.RichTextState
import com.mohamedrejeb.richeditor.ui.material.OutlinedRichTextEditor
@Composable @Composable
fun RichTextStyleRow( fun RichTextStyleRow(
@ -237,4 +238,18 @@ fun RichTextStyleButton(
) )
) )
} }
}
@Composable
fun RichTextField(
state: RichTextState,
modifier: Modifier = Modifier,
buttonsModifier: Modifier = Modifier,
outerModifier: Modifier = Modifier,
label: @Composable (() -> Unit)? = null
) = Column(outerModifier) {
RichTextStyleRow(buttonsModifier, state)
OutlinedRichTextEditor(
state = state, modifier = modifier, singleLine = false, minLines = 5, label = label
)
} }

View File

@ -34,6 +34,7 @@ import androidx.compose.ui.window.*
import com.jaytux.grader.data.Course import com.jaytux.grader.data.Course
import com.jaytux.grader.data.Edition import com.jaytux.grader.data.Edition
import com.jaytux.grader.viewmodel.PeerEvaluationState import com.jaytux.grader.viewmodel.PeerEvaluationState
import com.mohamedrejeb.richeditor.model.RichTextState
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.datetime.* import kotlinx.datetime.*
@ -211,7 +212,8 @@ fun PaneHeader(name: String, type: String, courseEdition: Pair<Course, Edition>)
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
fun AutocompleteLineField( fun AutocompleteLineField__(
// state: RichTextState,
value: TextFieldValue, onValueChange: (TextFieldValue) -> Unit, value: TextFieldValue, onValueChange: (TextFieldValue) -> Unit,
modifier: Modifier = Modifier, label: @Composable (() -> Unit)? = null, modifier: Modifier = Modifier, label: @Composable (() -> Unit)? = null,
onFilter: (String) -> List<String> onFilter: (String) -> List<String>