Commit 73dea68d authored by Christopher Lam's avatar Christopher Lam Committed by Chromium LUCI CQ

[style_var_gen] Generate colors in proto format.

This CL adds protobuf generation for semantic colors for Longform for use
in Google3. Generated values are provided in proto json format.

Bug: 1018654
Change-Id: I4f6648e41ab7fb1b205101bdb31362fd99ffb557
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2558100
Commit-Queue: calamity <calamity@chromium.org>
Reviewed-by: default avatarJiewei Qian  <qjw@chromium.org>
Cr-Commit-Position: refs/heads/master@{#836972}
parent 193d120c
......@@ -9,6 +9,7 @@ import re
import textwrap
import path_overrides
from color import Color
import copy
_FILE_PATH = os.path.dirname(os.path.realpath(__file__))
......@@ -56,6 +57,21 @@ class ModeVariables:
return self.variables[name][self._default_mode]
# Returns a Color that is the final RGBA value for |name| in |mode|.
def ResolveToRGBA(self, name, mode):
c = self.Resolve(name, mode)
if c.rgb_var:
result = Color()
result.a = c.a
rgb = self.ResolveToRGBA(c.RGBVarToVar(), mode)
(result.r, result.g, result.b) = (rgb.r, rgb.g, rgb.b)
return result
elif c.var:
return self.ResolveToRGBA(c.var, mode)
else:
return c
def keys(self):
return self.variables.keys()
......@@ -109,6 +125,9 @@ class BaseGenerator:
raise ValueError('Variable name "%s" is reused' % name)
self.context_map[name] = context or {}
def GetContextKey(self):
return self.GetName()
def AddColor(self, name, value_obj, context=None):
self._SetVariableContext(name, context)
try:
......@@ -146,7 +165,8 @@ class BaseGenerator:
object_pairs_hook=collections.OrderedDict)
# Use the generator's name to get the generator-specific context from
# the input.
generator_context = data.get('options', {}).get(self.GetName(), None)
generator_context = data.get('options',
{}).get(self.GetContextKey(), None)
self.in_file_to_context[in_file] = generator_context
for name, value in data['colors'].items():
......
......@@ -21,7 +21,7 @@ class Color:
with '_rgb'.
'''
def __init__(self, value_str):
def __init__(self, value_str=None):
# TODO(calamity): Add opacity-only values
self.var = None
self.rgb_var = None
......@@ -29,7 +29,8 @@ class Color:
self.g = -1
self.b = -1
self.a = 1
self.Parse(value_str)
if value_str is not None:
self.Parse(value_str)
def _AssignRGB(self, rgb):
for v in rgb:
......
......@@ -2,7 +2,11 @@
options: {
CSS: {
prefix: 'cros'
}
},
proto: {
field_name: 'test_colors',
field_id: 2,
},
},
colors: {
text_color_primary: {
......
/* This file is generated from:
* colors_test_palette.json5
* colors_test.json5
*/
syntax = "proto3";
package chromeos;
// Non-premultiplied 8-bit ARGB values.
message Color {
// Color value to use in light mode.
uint32 light_value = 1;
// Color value to use in dark mode.
uint32 dark_value = 2;
}
message Colors {
map<string, Color> palette_colors = 1;
map<string, Color> test_colors = 2;
}
/* This file is generated from:
* colors_test_palette.json5
* colors_test.json5
*/
palette_colors: {
google_grey_900: {
light_value: 0xFF202124,
dark_value: 0xFF202124
}
}
test_colors: {
text_color_primary: {
light_value: 0xFF202124,
dark_value: 0xFFFFFFFF
},
toggle_color: {
light_value: 0x19202124,
dark_value: 0x19FFFFFF
}
}
{
// No CSS prefix for this test palette.
options: {
// No CSS prefix for the google palette.
proto: {
field_name: 'palette_colors',
field_id: 1,
},
},
colors: {
google_grey_900: "#202124",
},
......
# Copyright 2019 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from css_generator import CSSStyleGenerator
from base_generator import Modes
import unittest
class CSSStyleGeneratorTest(unittest.TestCase):
def setUp(self):
self.generator = CSSStyleGenerator()
self.generator.AddJSONFileToModel('colors_test_palette.json5')
self.generator.AddJSONFileToModel('colors_test.json5')
def assertEqualToFile(self, value, filename):
with open(filename) as f:
contents = f.read()
self.assertEqual(
value, contents,
'\n>>>>>\n%s<<<<<\n\ndoes not match\n\n>>>>>\n%s<<<<<' %
(value, contents))
def testColorTestJSON(self):
self.assertEqualToFile(self.generator.Render(),
'colors_test_expected.css')
def testColorTestJSONDarkOnly(self):
self.generator.generate_single_mode = Modes.DARK
self.assertEqualToFile(self.generator.Render(),
'colors_test_dark_only_expected.css')
if __name__ == '__main__':
unittest.main()
# Copyright 2020 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import os
import collections
from base_generator import Color, Modes, BaseGenerator, VariableType
class BaseProtoStyleGenerator(BaseGenerator):
'''Base Generator for Protobuf formats'''
def GetParameters(self):
return {
'fields': self._CreateFieldList(),
}
def GetFilters(self):
return {
'proto_color': self._ProtoColor,
}
def GetGlobals(self):
return {
'Modes': Modes,
'in_files': self.in_file_to_context.keys(),
}
def _CreateFieldList(self):
field_value_map = dict()
field_list = []
for ctx in self.in_file_to_context.values():
field = {
'name': ctx['field_name'],
'id': ctx['field_id'],
'values': []
}
field_list.append(field)
field_value_map[field['name']] = field['values']
# Order fields by key
field_list.sort(key=lambda x: x['id'])
# Populate each field with its corresponding colors.
color_model = self.model[VariableType.COLOR]
for name, mode_values in color_model.items():
color_item = {
'name': name,
'mode_values': {
Modes.LIGHT: color_model.ResolveToRGBA(name, Modes.LIGHT),
Modes.DARK: color_model.ResolveToRGBA(name, Modes.DARK),
}
}
field_value_map[self.context_map[name]['field_name']].append(
color_item)
return field_list
def _ProtoColor(self, c):
'''Returns the proto color representation of |c|'''
assert (isinstance(c, Color))
def AlphaToInt(alpha):
return int(alpha * 255)
return '0x%X%02X%02X%02X' % (AlphaToInt(c.a), c.r, c.g, c.b)
class ProtoStyleGenerator(BaseProtoStyleGenerator):
@staticmethod
def GetName():
return 'proto'
def Render(self):
self.Validate()
return self.ApplyTemplate(self, 'proto_generator.tmpl',
self.GetParameters())
class ProtoJSONStyleGenerator(BaseProtoStyleGenerator):
@staticmethod
def GetName():
return 'protojson'
def GetContextKey(self):
return ProtoStyleGenerator.GetName()
def Render(self):
self.Validate()
return self.ApplyTemplate(self, 'proto_json_generator.tmpl',
self.GetParameters())
/* This file is generated from:
{%- for path in in_files %}
* {{path}}
{%- endfor %}
*/
syntax = "proto3";
package chromeos;
// Non-premultiplied 8-bit ARGB values.
message Color {
// Color value to use in light mode.
uint32 light_value = 1;
// Color value to use in dark mode.
uint32 dark_value = 2;
}
message Colors {
{%- for field in fields %}
map<string, Color> {{field.name}} = {{field.id}};
{%- endfor %}
}
/* This file is generated from:
{%- for path in in_files %}
* {{path}}
{%- endfor %}
*/
{% for field in fields -%}
{{field.name}}: {
{%- for obj in field['values'] %}
{{obj.name}}: {
light_value: {{obj.mode_values[Modes.LIGHT] | proto_color}},
dark_value: {{obj.mode_values[Modes.DARK] | proto_color}}
}{{ "," if not loop.last }}
{%- endfor %}
}
{% endfor -%}
......@@ -15,7 +15,17 @@
#
# foo_css
# CSS Variables
#
# foo_views
# Views generation for use with AshColorProvider
#
# foo_proto
# Protobuf message schema generation
#
# foo_protojson
# Protobuf JSON value format, contains actual color information
template("style_variable_generator") {
script_file = "//tools/style_variable_generator/style_variable_generator.py"
original_target_name = target_name
common_inputs = [
"//tools/style_variable_generator/base_generator.py",
......@@ -23,8 +33,7 @@ template("style_variable_generator") {
]
action("${target_name}_css") {
script = "//tools/style_variable_generator/style_variable_generator.py"
script = script_file
forward_variables_from(invoker,
[
"deps",
......@@ -46,8 +55,7 @@ template("style_variable_generator") {
}
action("${target_name}_views_generator") {
script = "//tools/style_variable_generator/style_variable_generator.py"
script = script_file
forward_variables_from(invoker,
[
"deps",
......@@ -71,4 +79,48 @@ template("style_variable_generator") {
sources = [ "$target_gen_dir/${original_target_name}.h" ]
deps = [ ":${original_target_name}_views_generator" ]
}
action("${target_name}_proto") {
script = script_file
forward_variables_from(invoker,
[
"deps",
"sources",
])
inputs = common_inputs + [
"//tools/style_variable_generator/proto_generator.py",
"//tools/style_variable_generator/proto_generator.tmpl",
]
out_file = "$target_gen_dir/${original_target_name}.proto"
outputs = [ out_file ]
args = [
"--generator=proto",
"--out-file",
rebase_path("$out_file", root_build_dir),
] + rebase_path(sources, root_build_dir)
}
action("${target_name}_protojson") {
script = script_file
forward_variables_from(invoker,
[
"deps",
"sources",
])
inputs = common_inputs + [
"//tools/style_variable_generator/proto_generator.py",
"//tools/style_variable_generator/proto_json_generator.tmpl",
]
out_file = "$target_gen_dir/${original_target_name}.protojson"
outputs = [ out_file ]
args = [
"--generator=proto",
"--out-file",
rebase_path("$out_file", root_build_dir),
] + rebase_path(sources, root_build_dir)
}
}
......@@ -5,6 +5,7 @@
import argparse
import sys
from css_generator import CSSStyleGenerator
from proto_generator import ProtoStyleGenerator, ProtoJSONStyleGenerator
from views_generator import ViewsStyleGenerator
from base_generator import Modes
......@@ -13,7 +14,10 @@ def main():
parser = argparse.ArgumentParser(
description='Generate style variables from JSON5 color file.')
generators = [CSSStyleGenerator, ViewsStyleGenerator]
generators = [
CSSStyleGenerator, ViewsStyleGenerator, ProtoStyleGenerator,
ProtoJSONStyleGenerator
]
parser.add_argument('--generator',
choices=[g.GetName() for g in generators],
......
......@@ -2,16 +2,13 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from css_generator import CSSStyleGenerator
from proto_generator import ProtoStyleGenerator, ProtoJSONStyleGenerator
from views_generator import ViewsStyleGenerator
import unittest
class ViewsStyleGeneratorTest(unittest.TestCase):
def setUp(self):
self.generator = ViewsStyleGenerator()
self.generator.AddJSONFileToModel('colors_test_palette.json5')
self.generator.AddJSONFileToModel('colors_test.json5')
class BaseStyleGeneratorTest:
def assertEqualToFile(self, value, filename):
with open(filename) as f:
contents = f.read()
......@@ -24,7 +21,39 @@ class ViewsStyleGeneratorTest(unittest.TestCase):
self.generator.out_file_path = (
'tools/style_variable_generator/colors_test_expected.h')
self.assertEqualToFile(self.generator.Render(),
'colors_test_expected.h')
self.expected_output_file)
class ViewsStyleGeneratorTest(unittest.TestCase, BaseStyleGeneratorTest):
def setUp(self):
self.generator = ViewsStyleGenerator()
self.generator.AddJSONFileToModel('colors_test_palette.json5')
self.generator.AddJSONFileToModel('colors_test.json5')
self.expected_output_file = 'colors_test_expected.h'
class CSSStyleGeneratorTest(unittest.TestCase, BaseStyleGeneratorTest):
def setUp(self):
self.generator = CSSStyleGenerator()
self.generator.AddJSONFileToModel('colors_test_palette.json5')
self.generator.AddJSONFileToModel('colors_test.json5')
self.expected_output_file = 'colors_test_expected.css'
class ProtoStyleGeneratorTest(unittest.TestCase, BaseStyleGeneratorTest):
def setUp(self):
self.generator = ProtoStyleGenerator()
self.generator.AddJSONFileToModel('colors_test_palette.json5')
self.generator.AddJSONFileToModel('colors_test.json5')
self.expected_output_file = 'colors_test_expected.proto'
class ProtoJSONStyleGeneratorTest(unittest.TestCase, BaseStyleGeneratorTest):
def setUp(self):
self.generator = ProtoJSONStyleGenerator()
self.generator.AddJSONFileToModel('colors_test_palette.json5')
self.generator.AddJSONFileToModel('colors_test.json5')
self.expected_output_file = 'colors_test_expected.protojson'
if __name__ == '__main__':
......
......@@ -13,6 +13,10 @@
CSS: {
prefix: 'cros',
},
proto: {
field_name: 'cros_colors',
field_id: 2,
},
},
colors: {
/*
......
......@@ -9,6 +9,12 @@
* here.
*/
{
options: {
proto: {
field_name: 'palette_colors',
field_id: 1,
},
},
colors: {
google_blue_50: "#e8f0fe",
google_blue_100: "#d2e3fc",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment