Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: make it possible to create a custom Markdown code theme #4343

Merged
merged 3 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions packages/flet/lib/src/controls/markdown.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:flutter_highlight/theme_map.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:markdown/markdown.dart' as md;

Expand Down Expand Up @@ -63,6 +62,8 @@ class MarkdownControl extends StatelessWidget with FletStoreMixin {
.copyWith(fontFamily: "monospace"));
var mdStyleSheet = parseMarkdownStyleSheet(
control, "mdStyleSheet", Theme.of(context), pageArgs);
var codeTheme =
parseMarkdownCodeTheme(control, "codeTheme", Theme.of(context));
Widget markdown = MarkdownBody(
data: value,
selectable: selectable,
Expand All @@ -71,8 +72,7 @@ class MarkdownControl extends StatelessWidget with FletStoreMixin {
: getBaseUri(pageArgs.pageUri!).toString(),
extensionSet: extensionSet,
builders: {
'code': CodeElementBuilder(
codeTheme.toLowerCase(), codeStyleSheet, selectable),
'code': CodeElementBuilder(codeTheme, codeStyleSheet, selectable),
},
styleSheet: mdStyleSheet,
imageBuilder: (Uri uri, String? title, String? alt) {
Expand Down Expand Up @@ -142,7 +142,7 @@ class MarkdownControl extends StatelessWidget with FletStoreMixin {
}

class CodeElementBuilder extends MarkdownElementBuilder {
final String codeTheme;
final Map<String, TextStyle> codeTheme;
final MarkdownStyleSheet mdStyleSheet;
final bool selectable;

Expand Down Expand Up @@ -175,7 +175,7 @@ class CodeElementBuilder extends MarkdownElementBuilder {

// Specify highlight theme
// All available themes are listed in `themes` folder
theme: themeMap[codeTheme] ?? {},
theme: codeTheme,

// Specify padding
padding: mdStyleSheet.codeblockPadding,
Expand Down
55 changes: 44 additions & 11 deletions packages/flet/lib/src/utils/markdown.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:flutter_highlight/theme_map.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:markdown/markdown.dart' as md;

Expand Down Expand Up @@ -29,19 +30,51 @@ md.ExtensionSet? parseMarkdownExtensionSet(String? value,
}
}

Map<String, TextStyle> parseMarkdownCodeTheme(
Control control,
String propName,
ThemeData theme,
) {
final v = control.attrString(propName);
if (v == null) {
return {};
}
dynamic j = json.decode(v);
if (j is String) {
return themeMap[j.toLowerCase()] ?? {};
} else if (j is Map<String, dynamic>) {
String transformKey(String key) {
switch (key) {
case 'class_name':
return 'class';
case 'built_in':
return key;
default:
return key.replaceAll('_', '-');
}
}

final resultMap =
j.map((key, value) => MapEntry(key, textStyleFromJson(theme, value)));
resultMap.removeWhere(
(key, value) => value == null); // remove entries with null values
return resultMap.map((key, value) => MapEntry(transformKey(key), value!));
}
return {};
}

MarkdownStyleSheet? parseMarkdownStyleSheet(Control control, String propName,
ThemeData theme, PageArgsModel? pageArgs) {
dynamic j;
var v = control.attrString(propName, null);
var v = control.attrString(propName);
if (v == null) {
return null;
}
j = json.decode(v);
dynamic j = json.decode(v);
return markdownStyleSheetFromJson(theme, j, pageArgs);
}

MarkdownStyleSheet markdownStyleSheetFromJson(ThemeData theme,
Map<String, dynamic> j, PageArgsModel? pageArgs) {
MarkdownStyleSheet markdownStyleSheetFromJson(
ThemeData theme, Map<String, dynamic> j, PageArgsModel? pageArgs) {
TextStyle? parseTextStyle(String propName) {
return j[propName] != null ? textStyleFromJson(theme, j[propName]) : null;
}
Expand Down Expand Up @@ -119,13 +152,13 @@ MarkdownStyleSheet markdownStyleSheetFromJson(ThemeData theme,
horizontalRuleDecoration: boxDecorationFromJSON(
theme, j["horizontal_rule_decoration"], pageArgs) ??
BoxDecoration(
border: Border(
top: BorderSide(
width: 5.0,
color: theme.dividerColor,
),
),
border: Border(
top: BorderSide(
width: 5.0,
color: theme.dividerColor,
),
),
),
blockquoteAlign:
parseWrapAlignment(j["blockquote_alignment"], WrapAlignment.start)!,
codeblockAlign:
Expand Down
1 change: 1 addition & 0 deletions sdk/python/packages/flet/src/flet/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@
from flet.core.markdown import (
Markdown,
MarkdownCodeTheme,
MarkdownCustomCodeTheme,
MarkdownExtensionSet,
MarkdownSelectionChangeCause,
MarkdownSelectionChangeEvent,
Expand Down
55 changes: 53 additions & 2 deletions sdk/python/packages/flet/src/flet/core/markdown.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,57 @@ class MarkdownCodeTheme(Enum):
ZENBURN = "zenburn"


@dataclass
class MarkdownCustomCodeTheme:
addition: Optional[TextStyle] = None
attr: Optional[TextStyle] = None
attribute: Optional[TextStyle] = None
built_in: Optional[TextStyle] = None
builtin_name: Optional[TextStyle] = None
bullet: Optional[TextStyle] = None
class_name: Optional[TextStyle] = None
code: Optional[TextStyle] = None
comment: Optional[TextStyle] = None
deletion: Optional[TextStyle] = None
doctag: Optional[TextStyle] = None
emphasis: Optional[TextStyle] = None
formula: Optional[TextStyle] = None
function: Optional[TextStyle] = None
keyword: Optional[TextStyle] = None
link: Optional[TextStyle] = None
link_label: Optional[TextStyle] = None
literal: Optional[TextStyle] = None
meta: Optional[TextStyle] = None
meta_keyword: Optional[TextStyle] = None
meta_string: Optional[TextStyle] = None
name: Optional[TextStyle] = None
number: Optional[TextStyle] = None
operator: Optional[TextStyle] = None
params: Optional[TextStyle] = None
pattern_match: Optional[TextStyle] = None
quote: Optional[TextStyle] = None
regexp: Optional[TextStyle] = None
root: Optional[TextStyle] = None
section: Optional[TextStyle] = None
selector_attr: Optional[TextStyle] = None
selector_class: Optional[TextStyle] = None
selector_id: Optional[TextStyle] = None
selector_pseudo: Optional[TextStyle] = None
selector_tag: Optional[TextStyle] = None
string: Optional[TextStyle] = None
strong: Optional[TextStyle] = None
stronge: Optional[TextStyle] = None
subst: Optional[TextStyle] = None
subtr: Optional[TextStyle] = None
symbol: Optional[TextStyle] = None
tag: Optional[TextStyle] = None
template_tag: Optional[TextStyle] = None
template_variable: Optional[TextStyle] = None
title: Optional[TextStyle] = None
type: Optional[TextStyle] = None
variable: Optional[TextStyle] = None


class Markdown(ConstrainedControl):
"""
Control for rendering text in markdown format.
Expand All @@ -239,7 +290,7 @@ def __init__(
value: Optional[str] = None,
selectable: Optional[bool] = None,
extension_set: Optional[MarkdownExtensionSet] = None,
code_theme: Optional[MarkdownCodeTheme] = None,
code_theme: Optional[Union[MarkdownCodeTheme, MarkdownCustomCodeTheme]] = None,
code_style: Optional[TextStyle] = None,
auto_follow_links: Optional[bool] = None,
shrink_wrap: Optional[bool] = None,
Expand Down Expand Up @@ -349,6 +400,7 @@ def before_update(self):
self._set_attr_json("codeStyle", self.__code_style)
self._set_attr_json("codeStyleSheet", self.__code_style_sheet)
self._set_attr_json("mdStyleSheet", self.__md_style_sheet)
self._set_attr_json("codeTheme", self.__code_theme)

def _get_children(self):
if self.__img_error_content is not None:
Expand Down Expand Up @@ -437,7 +489,6 @@ def code_theme(self) -> Optional[MarkdownCodeTheme]:
@code_theme.setter
def code_theme(self, value: Optional[MarkdownCodeTheme]):
self.__code_theme = value
self._set_enum_attr("codeTheme", value, MarkdownCodeTheme)

# code_style
@property
Expand Down